mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Update Doxyfile to version 1.9.1
Re-enable doxygen CI checker
Fix some regular comments that should have been doc comments
Document void return types for some inline functions explicitly
Move start of some doxygen groups so they are included always, and not left
out of certain ifdefs
Ignore some doxygen warnings:
Member (function) is not documented in headers
Documented empty return type in headers
Argument has multiple @param documentation sections
JerryScript-DCO-1.0-Signed-off-by: Máté Tokodi mate.tokodi@szteszoftver.hu
2960 lines
93 KiB
C
2960 lines
93 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);
|
|
|
|
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);
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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:
|
|
case SCANNER_STREAM_TYPE_LET:
|
|
case SCANNER_STREAM_TYPE_CONST:
|
|
case SCANNER_STREAM_TYPE_LOCAL:
|
|
#if JERRY_MODULE_SYSTEM
|
|
case SCANNER_STREAM_TYPE_IMPORT:
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
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:
|
|
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 */
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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;
|
|
|
|
const uint16_t copied_flags =
|
|
(SCANNER_LITERAL_POOL_IN_WITH | SCANNER_LITERAL_POOL_GENERATOR | SCANNER_LITERAL_POOL_ASYNC);
|
|
|
|
status_flags |= (uint16_t) (prev_literal_pool_p->status_flags & copied_flags);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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 (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;
|
|
}
|
|
}
|
|
|
|
uint8_t can_eval_types = 0;
|
|
|
|
if (prev_literal_pool_p == NULL && !(context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL))
|
|
{
|
|
can_eval_types |= SCANNER_LITERAL_IS_FUNC;
|
|
}
|
|
|
|
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))
|
|
{
|
|
JERRY_ASSERT (arguments_type != SCANNER_ARGUMENTS_NOT_PRESENT);
|
|
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;
|
|
}
|
|
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
|
|
{
|
|
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--;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* The SCANNER_LITERAL_IS_ARG may be set above. */
|
|
if (!(type & SCANNER_LITERAL_IS_ARG))
|
|
{
|
|
literal_p->type = 0;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else if (type & SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
last_argument_p = literal_p;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 ((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--;
|
|
}
|
|
}
|
|
|
|
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)
|
|
|| ((type & SCANNER_LITERAL_IS_FUNC) && (status_flags & SCANNER_LITERAL_POOL_IS_STRICT))
|
|
|| !(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 ((status_flags & (SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_CLASS_FIELD))
|
|
|| (type & SCANNER_LITERAL_NO_REG))
|
|
{
|
|
extended_type |= SCANNER_LITERAL_NO_REG;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 (!(status_flags & (SCANNER_LITERAL_POOL_IS_STRICT | SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT)))
|
|
{
|
|
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 (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;
|
|
}
|
|
|
|
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 (literal_p->type & SCANNER_LITERAL_IS_DESTRUCTURED_ARG)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC;
|
|
}
|
|
}
|
|
}
|
|
else if (literal_p->type & SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_ARG;
|
|
|
|
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);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
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 (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;
|
|
}
|
|
}
|
|
|
|
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 */
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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_UNLIKELY (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_CLASS_NAME))
|
|
{
|
|
literal_pool_p = literal_pool_p->prev_p;
|
|
continue;
|
|
}
|
|
|
|
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);
|
|
lit_location_p->type |= SCANNER_LITERAL_IS_USED;
|
|
|
|
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 (literal_p->type & SCANNER_LITERAL_IS_USED)
|
|
{
|
|
literal_type = SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_EARLY_CREATE;
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
scanner_context_p->active_literal_pool_p->status_flags |=
|
|
(SCANNER_LITERAL_POOL_CAN_EVAL | SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE);
|
|
}
|
|
} /* scanner_detect_eval_call */
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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:
|
|
case SCANNER_TYPE_FOR_OF:
|
|
case SCANNER_TYPE_CASE:
|
|
case SCANNER_TYPE_INITIALIZER:
|
|
case SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END:
|
|
case SCANNER_TYPE_CLASS_STATIC_BLOCK_END:
|
|
{
|
|
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;
|
|
}
|
|
case SCANNER_TYPE_CLASS_CONSTRUCTOR:
|
|
{
|
|
scanner_release_private_fields (((scanner_class_info_t *) scanner_info_p)->members);
|
|
size = sizeof (scanner_class_info_t);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
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);
|
|
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);
|
|
|
|
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 */
|
|
|
|
while (data_p[0] != SCANNER_STREAM_TYPE_END)
|
|
{
|
|
uint8_t data = data_p[0];
|
|
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 */
|
|
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_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;
|
|
}
|
|
}
|
|
|
|
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 */
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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 */
|
|
|
|
/**
|
|
* 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);
|
|
|
|
scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1);
|
|
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 (data_p[0] & SCANNER_STREAM_LOCAL_ARGUMENTS)
|
|
{
|
|
context_p->status_flags |= PARSER_LEXICAL_BLOCK_NEEDED;
|
|
}
|
|
|
|
scope_stack_p->map_to = 0;
|
|
}
|
|
|
|
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 (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);
|
|
}
|
|
|
|
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 ((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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
JERRY_ASSERT (!(context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS));
|
|
|
|
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_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;
|
|
}
|
|
|
|
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 (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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1);
|
|
scope_stack_reg_top++;
|
|
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED;
|
|
map_to = context_p->lit_object.index;
|
|
|
|
uint16_t scope_stack_map_to = 0;
|
|
|
|
if (info_type == SCANNER_TYPE_FUNCTION)
|
|
{
|
|
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
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:
|
|
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 */
|
|
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;
|
|
}
|
|
}
|
|
|
|
parser_emit_cbc_literal (context_p, opcode, map_to);
|
|
break;
|
|
}
|
|
case SCANNER_STREAM_TYPE_ARG:
|
|
case SCANNER_STREAM_TYPE_ARG_VAR:
|
|
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 */
|
|
|
|
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))
|
|
{
|
|
parser_emit_cbc_literal_value (context_p,
|
|
CBC_INIT_ARG_OR_FUNC,
|
|
(uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top),
|
|
map_to);
|
|
}
|
|
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;
|
|
}
|
|
|
|
if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
scope_stack_reg_top++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
scope_stack_p->map_to = scope_stack_map_to;
|
|
}
|
|
|
|
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))
|
|
{
|
|
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];
|
|
}
|
|
|
|
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 (info_type == SCANNER_TYPE_FUNCTION)
|
|
{
|
|
context_p->scope_stack_global_end = context_p->scope_stack_top;
|
|
}
|
|
|
|
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);
|
|
|
|
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));
|
|
} /* scanner_decode_map_to */
|
|
|
|
/**
|
|
* 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_PARSER */
|