Fix lexical scoping between scripts (#3558)
This change fixes the handling of lexical blocks when executing multiple scripts, and also fixes a few issues with module environments. After this change, all script files will run in the same context and will have access to lexically scoped global variables of previous scripts, and module environments will no longer have a bound global 'this' value. The REPL implementation in main-unix is also fixed to correctly handle lexically scoped variables. Fixes #3561. JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
This commit is contained in:
@@ -302,6 +302,10 @@
|
||||
VM_OC_NEW | VM_OC_PUT_STACK) \
|
||||
CBC_OPCODE (CBC_EVAL, CBC_NO_FLAG, 0, \
|
||||
VM_OC_EVAL) \
|
||||
CBC_OPCODE (CBC_CHECK_VAR, CBC_HAS_LITERAL_ARG, 0, \
|
||||
VM_OC_CHECK_VAR) \
|
||||
CBC_OPCODE (CBC_CHECK_LET, CBC_HAS_LITERAL_ARG, 0, \
|
||||
VM_OC_CHECK_LET) \
|
||||
CBC_OPCODE (CBC_CREATE_VAR, CBC_HAS_LITERAL_ARG, 0, \
|
||||
VM_OC_CREATE_BINDING) \
|
||||
CBC_OPCODE (CBC_CREATE_LET, CBC_HAS_LITERAL_ARG, 0, \
|
||||
@@ -780,6 +784,7 @@ typedef enum
|
||||
CBC_CODE_FLAGS_GENERATOR = (1u << 11), /**< this function is a generator */
|
||||
CBC_CODE_FLAGS_REST_PARAMETER = (1u << 12), /**< this function has rest parameter */
|
||||
CBC_CODE_FLAG_HAS_TAGGED_LITERALS = (1u << 13), /**< this function has tagged template literal list */
|
||||
CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED = (1u << 14), /**< compiled code needs a lexical block */
|
||||
} cbc_code_flags;
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef enum
|
||||
PARSER_DEBUGGER_BREAKPOINT_APPENDED = (1u << 11), /**< pending (unsent) breakpoint
|
||||
* info is available */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
PARSER_INSIDE_BLOCK = (1u << 12), /**< script has a lexical environment for let and const */
|
||||
PARSER_LEXICAL_BLOCK_NEEDED = (1u << 12), /**< script needs a lexical environment for let and const */
|
||||
PARSER_IS_ARROW_FUNCTION = (1u << 13), /**< an arrow function is parsed */
|
||||
PARSER_IS_GENERATOR_FUNCTION = (1u << 14), /**< a generator function is parsed */
|
||||
PARSER_IS_ASYNC_FUNCTION = (1u << 15), /**< an async function is parsed */
|
||||
@@ -726,6 +726,7 @@ bool scanner_is_context_needed (parser_context_t *context_p);
|
||||
bool scanner_is_global_context_needed (parser_context_t *context_p);
|
||||
bool scanner_scope_find_let_declaration (parser_context_t *context_p, lexer_lit_location_t *literal_p);
|
||||
bool scanner_try_scan_new_target (parser_context_t *context_p);
|
||||
void scanner_check_variables (parser_context_t *context_p);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
void scanner_create_variables (parser_context_t *context_p, uint32_t option_flags);
|
||||
|
||||
|
||||
@@ -325,8 +325,8 @@ parser_module_context_init (void)
|
||||
ecma_module_t *module_p = ecma_module_find_or_create_module (path_p);
|
||||
|
||||
module_p->state = ECMA_MODULE_STATE_EVALUATED;
|
||||
module_p->scope_p = ecma_get_global_environment ();
|
||||
ecma_ref_object (module_p->scope_p);
|
||||
/* The lexical scope of the root module does not exist yet. */
|
||||
module_p->scope_p = NULL;
|
||||
|
||||
module_p->context_p = module_context_p;
|
||||
module_context_p->module_p = module_p;
|
||||
|
||||
@@ -125,21 +125,7 @@ static const uint8_t parser_statement_flags[] =
|
||||
PARSER_STATM_CONTEXT_BREAK
|
||||
};
|
||||
|
||||
#if !ENABLED (JERRY_ES2015)
|
||||
|
||||
/**
|
||||
* Get the expected depth of a function call.
|
||||
*/
|
||||
#define JERRY_GET_EXPECTED_DEPTH(context_p) 0
|
||||
|
||||
#else /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/**
|
||||
* Get the expected depth of a function call.
|
||||
*/
|
||||
#define JERRY_GET_EXPECTED_DEPTH(context_p) \
|
||||
(((context_p)->status_flags & PARSER_INSIDE_BLOCK) ? PARSER_BLOCK_CONTEXT_STACK_ALLOCATION : 0)
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/**
|
||||
* Block statement.
|
||||
*/
|
||||
@@ -2637,7 +2623,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
lexer_lit_location_t lit_location;
|
||||
bool is_use_strict = false;
|
||||
|
||||
JERRY_ASSERT (context_p->stack_depth == JERRY_GET_EXPECTED_DEPTH (context_p));
|
||||
JERRY_ASSERT (context_p->stack_depth == 0);
|
||||
#ifndef JERRY_NDEBUG
|
||||
JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
@@ -3348,7 +3334,7 @@ consume_last_statement:
|
||||
}
|
||||
}
|
||||
|
||||
JERRY_ASSERT (context_p->stack_depth == JERRY_GET_EXPECTED_DEPTH (context_p));
|
||||
JERRY_ASSERT (context_p->stack_depth == 0);
|
||||
#ifndef JERRY_NDEBUG
|
||||
JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
@@ -1311,6 +1311,11 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_CODE_FLAG_HAS_TAGGED_LITERALS;
|
||||
}
|
||||
|
||||
if (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED)
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count;
|
||||
@@ -2093,18 +2098,12 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (scanner_is_global_context_needed (&context))
|
||||
{
|
||||
parser_branch_t branch;
|
||||
context.status_flags |= PARSER_LEXICAL_BLOCK_NEEDED;
|
||||
}
|
||||
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_PLUS_EQUAL_U16 (context.context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_emit_cbc_forward_branch (&context,
|
||||
CBC_BLOCK_CREATE_CONTEXT,
|
||||
&branch);
|
||||
|
||||
parser_stack_push (&context, &branch, sizeof (parser_branch_t));
|
||||
context.status_flags |= PARSER_INSIDE_BLOCK;
|
||||
if ((parse_opts & ECMA_PARSE_EVAL) == 0)
|
||||
{
|
||||
scanner_check_variables (&context);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
@@ -2113,18 +2112,6 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
|
||||
|
||||
parser_parse_statements (&context);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (context.status_flags & PARSER_INSIDE_BLOCK)
|
||||
{
|
||||
parser_branch_t branch;
|
||||
parser_stack_pop (&context, &branch, sizeof (parser_branch_t));
|
||||
|
||||
parser_emit_cbc (&context, CBC_CONTEXT_END);
|
||||
parser_set_branch_to_current_position (&context, &branch);
|
||||
parser_flush_cbc (&context);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/* When the parsing is successful, only the
|
||||
* dummy value can be remained on the stack. */
|
||||
JERRY_ASSERT (context.stack_top_uint8 == CBC_MAXIMUM_BYTE_VALUE
|
||||
|
||||
@@ -257,9 +257,6 @@ typedef enum
|
||||
SCANNER_LITERAL_POOL_BLOCK = (1 << 1), /**< literal pool represents a code block */
|
||||
SCANNER_LITERAL_POOL_IS_STRICT = (1 << 2), /**< literal pool represents a strict mode code block */
|
||||
SCANNER_LITERAL_POOL_NO_REG = (1 << 3), /**< variable declarations cannot be kept in registers */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
SCANNER_LITERAL_POOL_NO_VAR_REG = (1 << 4), /**< non let/const declarations cannot be kept in registers */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
SCANNER_LITERAL_POOL_NO_ARGUMENTS = (1 << 5), /**< arguments object must not be constructed */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
SCANNER_LITERAL_POOL_ARGUMENTS_UNMAPPED = (1 << 6), /**< arguments object should be unmapped */
|
||||
|
||||
@@ -470,21 +470,14 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */
|
||||
bool no_reg = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_REG) != 0;
|
||||
bool search_arguments = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) == 0;
|
||||
bool arguments_required = (no_reg && search_arguments);
|
||||
|
||||
uint8_t no_reg_types = 0;
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
uint8_t no_var_flags = 0;
|
||||
|
||||
if (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_VAR_REG)
|
||||
if (!(context_p->status_flags & PARSER_IS_DIRECT_EVAL))
|
||||
{
|
||||
no_var_flags = SCANNER_LITERAL_IS_VAR;
|
||||
|
||||
if (!(context_p->status_flags & PARSER_IS_DIRECT_EVAL))
|
||||
{
|
||||
no_var_flags |= SCANNER_LITERAL_IS_FUNC;
|
||||
}
|
||||
no_reg_types |= SCANNER_LITERAL_IS_FUNC;
|
||||
}
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
uint8_t no_var_flags = 0;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
#endif /* !ENABLED (JERRY_ES2015) */
|
||||
|
||||
if (no_reg && prev_literal_pool_p != NULL)
|
||||
{
|
||||
@@ -565,7 +558,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */
|
||||
no_declarations++;
|
||||
}
|
||||
|
||||
if (no_reg || (type & no_var_flags))
|
||||
if (no_reg || (type & no_reg_types))
|
||||
{
|
||||
type |= SCANNER_LITERAL_NO_REG;
|
||||
literal_p->type = type;
|
||||
@@ -1738,6 +1731,90 @@ scanner_create_unused_literal (parser_context_t *context_p, /**< context */
|
||||
context_p->literal_count++;
|
||||
} /* scanner_create_unused_literal */
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/**
|
||||
* 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 (const uint8_t *));
|
||||
next_data_p += 2 + 1 + sizeof (const uint8_t *);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8;
|
||||
|
||||
if (diff <= UINT8_MAX)
|
||||
{
|
||||
diff = -diff;
|
||||
}
|
||||
|
||||
literal.char_p += diff;
|
||||
next_data_p += 2 + 2;
|
||||
}
|
||||
|
||||
literal.length = data_p[1];
|
||||
literal.type = LEXER_IDENT_LITERAL;
|
||||
literal.has_escape = (data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? 1 : 0;
|
||||
|
||||
lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL);
|
||||
literal.char_p += data_p[1];
|
||||
|
||||
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
||||
if (type == SCANNER_STREAM_TYPE_IMPORT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
|
||||
|
||||
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED;
|
||||
|
||||
uint16_t opcode;
|
||||
if (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_FUNC)
|
||||
{
|
||||
opcode = CBC_CHECK_VAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
opcode = CBC_CHECK_LET;
|
||||
}
|
||||
|
||||
parser_emit_cbc_literal (context_p, opcode, context_p->lit_object.index);
|
||||
}
|
||||
|
||||
parser_flush_cbc (context_p);
|
||||
} /* scanner_check_variables */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/**
|
||||
* Create and/or initialize var/let/const/function/etc. variables.
|
||||
*/
|
||||
|
||||
@@ -2263,11 +2263,7 @@ scanner_scan_all (parser_context_t *context_p, /**< context */
|
||||
context_p->source_p = source_p;
|
||||
context_p->source_end_p = source_end_p;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_VAR_REG;
|
||||
#else /* !ENABLED (JERRY_DEBUGGER) */
|
||||
uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_REG;
|
||||
#endif /* ENABLED (JERRY_DEBUGGER) */
|
||||
|
||||
if (context_p->status_flags & PARSER_IS_STRICT)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user