Fix named function expression creation. (#2634)

Create a local lexical environment with the name of the function. While
this is not too memory efficient, some corner cases requires its existence.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg 2018-12-12 12:50:03 +01:00 committed by Akos Kiss
parent 4f0b075f85
commit 83ee9cfca3
11 changed files with 182 additions and 147 deletions

View File

@ -121,6 +121,8 @@ ecma_object_t *
ecma_op_create_function_object (ecma_object_t *scope_p, /**< function's scope */
const ecma_compiled_code_t *bytecode_data_p) /**< byte-code array */
{
JERRY_ASSERT (ecma_is_lexical_environment (scope_p));
/* 1., 4., 13. */
ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE);

View File

@ -560,6 +560,8 @@
-1 + PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION, VM_OC_CLASS_HERITAGE) \
\
/* Basic opcodes. */ \
CBC_OPCODE (CBC_EXT_PUSH_NAMED_FUNC_EXPRESSION, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 1, \
VM_OC_PUSH_NAMED_FUNC_EXPR | VM_OC_GET_LITERAL_LITERAL) \
CBC_OPCODE (CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_0, CBC_HAS_LITERAL_ARG, 2, \
VM_OC_PUSH_LIT_0 | VM_OC_GET_LITERAL) \
CBC_OPCODE (CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE, CBC_HAS_LITERAL_ARG | CBC_HAS_BYTE_ARG, 2, \

View File

@ -63,13 +63,12 @@ typedef enum
LEXER_FLAG_VAR = (1 << 0), /**< local identifier (var, function arg) */
LEXER_FLAG_NO_REG_STORE = (1 << 1), /**< this local identifier cannot be stored in register */
LEXER_FLAG_INITIALIZED = (1 << 2), /**< this local identifier is initialized with a value */
LEXER_FLAG_FUNCTION_NAME = (1 << 3), /**< this local identifier has a reference to the function itself */
LEXER_FLAG_FUNCTION_ARGUMENT = (1 << 4), /**< this local identifier is a function argument */
LEXER_FLAG_UNUSED_IDENT = (1 << 5), /**< this identifier is referenced by sub-functions,
LEXER_FLAG_FUNCTION_ARGUMENT = (1 << 3), /**< this local identifier is a function argument */
LEXER_FLAG_UNUSED_IDENT = (1 << 4), /**< this identifier is referenced by sub-functions,
* but not referenced by the currently parsed function */
LEXER_FLAG_SOURCE_PTR = (1 << 6), /**< the literal is directly referenced in the source code
LEXER_FLAG_SOURCE_PTR = (1 << 5), /**< the literal is directly referenced in the source code
* (no need to allocate memory) */
LEXER_FLAG_LATE_INIT = (1 << 7), /**< initialize this variable after the byte code is freed */
LEXER_FLAG_LATE_INIT = (1 << 6), /**< initialize this variable after the byte code is freed */
} lexer_literal_status_flags_t;
/**

View File

@ -1742,35 +1742,33 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */
context_p->lit_object.type = LEXER_LITERAL_OBJECT_ANY;
if (literal_type == LEXER_IDENT_LITERAL)
if (literal_type == LEXER_IDENT_LITERAL
&& (context_p->status_flags & PARSER_INSIDE_WITH)
&& context_p->lit_object.literal_p->type == LEXER_IDENT_LITERAL)
{
if ((context_p->status_flags & PARSER_INSIDE_WITH)
&& context_p->lit_object.literal_p->type == LEXER_IDENT_LITERAL)
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE;
}
if (literal_p->length == 4
&& source_p[0] == LIT_CHAR_LOWERCASE_E
&& source_p[3] == LIT_CHAR_LOWERCASE_L
&& source_p[1] == LIT_CHAR_LOWERCASE_V
&& source_p[2] == LIT_CHAR_LOWERCASE_A)
{
context_p->lit_object.type = LEXER_LITERAL_OBJECT_EVAL;
}
if (literal_p->length == 9
&& source_p[0] == LIT_CHAR_LOWERCASE_A
&& source_p[8] == LIT_CHAR_LOWERCASE_S
&& memcmp (source_p + 1, "rgument", 7) == 0)
{
context_p->lit_object.type = LEXER_LITERAL_OBJECT_ARGUMENTS;
if (!(context_p->status_flags & PARSER_ARGUMENTS_NOT_NEEDED))
{
context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED;
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE;
}
if (literal_p->length == 4
&& source_p[0] == LIT_CHAR_LOWERCASE_E
&& source_p[3] == LIT_CHAR_LOWERCASE_L
&& source_p[1] == LIT_CHAR_LOWERCASE_V
&& source_p[2] == LIT_CHAR_LOWERCASE_A)
{
context_p->lit_object.type = LEXER_LITERAL_OBJECT_EVAL;
}
if (literal_p->length == 9
&& source_p[0] == LIT_CHAR_LOWERCASE_A
&& source_p[8] == LIT_CHAR_LOWERCASE_S
&& memcmp (source_p + 1, "rgument", 7) == 0)
{
context_p->lit_object.type = LEXER_LITERAL_OBJECT_ARGUMENTS;
if (!(context_p->status_flags & PARSER_ARGUMENTS_NOT_NEEDED))
{
context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED;
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE;
}
}
}
if (destination_start_p != local_byte_array)

View File

@ -889,6 +889,52 @@ parser_parse_function_expression (parser_context_t *context_p, /**< context */
uint16_t literal1 = 0;
uint16_t literal2 = 0;
uint16_t function_literal_index;
int32_t function_name_index = -1;
if (status_flags & PARSER_IS_FUNC_EXPRESSION)
{
#ifdef JERRY_DEBUGGER
parser_line_counter_t debugger_line = context_p->token.line;
parser_line_counter_t debugger_column = context_p->token.column;
#endif /* JERRY_DEBUGGER */
if (!lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN))
{
lexer_next_token (context_p);
if (context_p->token.type != LEXER_LITERAL
|| context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
{
parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED);
}
parser_flush_cbc (context_p);
lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL);
#ifdef JERRY_DEBUGGER
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME,
JERRY_DEBUGGER_NO_SUBTYPE,
context_p->lit_object.literal_p->u.char_p,
context_p->lit_object.literal_p->prop.length);
/* Reset token position for the function. */
context_p->token.line = debugger_line;
context_p->token.column = debugger_column;
}
#endif /* JERRY_DEBUGGER */
if (context_p->token.literal_is_reserved
|| context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY)
{
status_flags |= PARSER_HAS_NON_STRICT_ARG;
}
function_name_index = context_p->lit_object.index;
}
}
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
@ -930,6 +976,12 @@ parser_parse_function_expression (parser_context_t *context_p, /**< context */
parser_emit_cbc_literal (context_p,
CBC_PUSH_LITERAL,
function_literal_index);
if (function_name_index != -1)
{
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_NAMED_FUNC_EXPRESSION);
context_p->last_cbc.value = (uint16_t) function_name_index;
}
}
context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL;

View File

@ -45,7 +45,6 @@ typedef enum
PARSER_IS_FUNC_EXPRESSION = (1u << 3), /**< a function expression is parsed */
PARSER_IS_PROPERTY_GETTER = (1u << 4), /**< a property getter function is parsed */
PARSER_IS_PROPERTY_SETTER = (1u << 5), /**< a property setter function is parsed */
PARSER_NAMED_FUNCTION_EXP = (1u << 6), /**< a function expression has a name binding */
PARSER_HAS_NON_STRICT_ARG = (1u << 7), /**< the function has arguments which
* are not supported in strict mode */
PARSER_ARGUMENTS_NEEDED = (1u << 8), /**< arguments object must be created */

View File

@ -413,7 +413,7 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */
if (name_p->status_flags & LEXER_FLAG_INITIALIZED)
{
if (!(name_p->status_flags & (LEXER_FLAG_FUNCTION_NAME | LEXER_FLAG_FUNCTION_ARGUMENT)))
if (!(name_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT))
{
/* Overwrite the previous initialization. */
ecma_compiled_code_t *compiled_code_p;

View File

@ -232,17 +232,6 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */
if (literal_p->status_flags & LEXER_FLAG_INITIALIZED)
{
if (literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME)
{
JERRY_ASSERT (literal_p == PARSER_GET_LITERAL (0));
status_flags |= PARSER_NAMED_FUNCTION_EXP;
context_p->status_flags = status_flags;
literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE;
context_p->literal_count++;
}
if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)
{
if ((status_flags & PARSER_ARGUMENTS_NEEDED)
@ -460,14 +449,11 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */
init_index = literal_index;
literal_index++;
if (!(literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME))
{
lexer_literal_t *func_literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator);
lexer_literal_t *func_literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator);
JERRY_ASSERT (func_literal_p != NULL
&& func_literal_p->type == LEXER_FUNCTION_LITERAL);
func_literal_p->prop.index = init_index;
}
JERRY_ASSERT (func_literal_p != NULL
&& func_literal_p->type == LEXER_FUNCTION_LITERAL);
func_literal_p->prop.index = init_index;
}
/* A CBC_INITIALIZE_VAR instruction or part of a CBC_INITIALIZE_VARS instruction. */
@ -547,7 +533,6 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */
ecma_value_t *literal_pool_p, /**< start of literal pool */
uint16_t uninitialized_var_end, /**< end of the uninitialized var group */
uint16_t initialized_var_end, /**< end of the initialized var group */
uint16_t const_literal_end, /**< end of the const literal group */
uint16_t literal_one_byte_limit) /**< maximum value of a literal
* encoded in one byte */
{
@ -599,12 +584,7 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */
#endif /* !JERRY_NDEBUG */
literal_p->status_flags = (uint8_t) (literal_p->status_flags & ~LEXER_FLAG_INITIALIZED);
if (literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME)
{
init_index = const_literal_end;
}
else if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)
if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)
{
init_index = (uint16_t) (argument_count - 1);
}
@ -691,11 +671,7 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */
JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL);
if (literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME)
{
init_index = const_literal_end;
}
else if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)
if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)
{
init_index = (uint16_t) (argument_count - 1);
@ -1840,7 +1816,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */
literal_pool_p,
uninitialized_var_end,
initialized_var_end,
const_literal_end,
literal_one_byte_limit);
JERRY_ASSERT (dst_p == byte_code_p + initializers_length);
@ -2133,12 +2108,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */
}
#endif /* JERRY_ENABLE_LINE_INFO */
if (context_p->status_flags & PARSER_NAMED_FUNCTION_EXP)
{
ECMA_SET_INTERNAL_VALUE_POINTER (literal_pool_p[const_literal_end],
compiled_code_p);
}
#ifdef JERRY_DEBUGGER
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
@ -2612,51 +2581,9 @@ parser_parse_function (parser_context_t *context_p, /**< context */
}
#endif /* PARSER_DUMP_BYTE_CODE */
#ifdef JERRY_DEBUGGER
parser_line_counter_t debugger_line = context_p->token.line;
parser_line_counter_t debugger_column = context_p->token.column;
#endif /* JERRY_DEBUGGER */
lexer_next_token (context_p);
if (context_p->status_flags & PARSER_IS_FUNC_EXPRESSION
&& context_p->token.type == LEXER_LITERAL
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
{
lexer_construct_literal_object (context_p,
&context_p->token.lit_location,
LEXER_IDENT_LITERAL);
#ifdef JERRY_DEBUGGER
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME,
JERRY_DEBUGGER_NO_SUBTYPE,
context_p->lit_object.literal_p->u.char_p,
context_p->lit_object.literal_p->prop.length);
}
#endif /* JERRY_DEBUGGER */
/* The arguments object is created later than the binding to the
* function expression name, so there is no need to assign special flags. */
if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ARGUMENTS)
{
uint8_t lexer_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_NAME;
context_p->lit_object.literal_p->status_flags |= lexer_flags;
}
if (context_p->token.literal_is_reserved
|| context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY)
{
context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG;
}
lexer_next_token (context_p);
}
#ifdef JERRY_DEBUGGER
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
&& jerry_debugger_send_parse_function (debugger_line, debugger_column))
&& jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column))
{
/* This option has a high memory and performance costs,
* but it is necessary for executing eval operations by the debugger. */
@ -2664,6 +2591,8 @@ parser_parse_function (parser_context_t *context_p, /**< context */
}
#endif /* JERRY_DEBUGGER */
lexer_next_token (context_p);
if (context_p->token.type != LEXER_LEFT_PAREN)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED);

View File

@ -714,17 +714,6 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
uint16_t register_end;
ecma_value_t *literal_start_p = frame_ctx_p->literal_start_p;
bool is_strict = ((frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0);
ecma_value_t self_reference;
#ifdef JERRY_ENABLE_SNAPSHOT_EXEC
self_reference = 0;
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION))
{
ECMA_SET_INTERNAL_VALUE_POINTER (self_reference, bytecode_header_p);
}
#else /* !JERRY_ENABLE_SNAPSHOT_EXEC */
ECMA_SET_INTERNAL_VALUE_POINTER (self_reference, bytecode_header_p);
#endif
/* Prepare. */
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING))
@ -793,7 +782,6 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
{
uint32_t value_index;
ecma_value_t lit_value;
bool is_immutable_binding = false;
READ_LITERAL_INDEX (value_index);
@ -803,7 +791,6 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
}
else
{
is_immutable_binding = (self_reference == literal_start_p[value_index]);
lit_value = vm_construct_literal_object (frame_ctx_p,
literal_start_p[value_index]);
}
@ -816,29 +803,22 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
{
ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]);
if (JERRY_LIKELY (!is_immutable_binding))
vm_var_decl (frame_ctx_p, name_p);
ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, name_p);
ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (ref_base_lex_env_p,
name_p,
is_strict,
lit_value);
JERRY_ASSERT (ecma_is_value_boolean (put_value_result)
|| ecma_is_value_empty (put_value_result)
|| ECMA_IS_VALUE_ERROR (put_value_result));
if (ECMA_IS_VALUE_ERROR (put_value_result))
{
vm_var_decl (frame_ctx_p, name_p);
ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, name_p);
ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (ref_base_lex_env_p,
name_p,
is_strict,
lit_value);
JERRY_ASSERT (ecma_is_value_boolean (put_value_result)
|| ecma_is_value_empty (put_value_result)
|| ECMA_IS_VALUE_ERROR (put_value_result));
if (ECMA_IS_VALUE_ERROR (put_value_result))
{
ecma_free_value (JERRY_CONTEXT (error_value));
}
}
else
{
ecma_op_create_immutable_binding (frame_ctx_p->lex_env_p, name_p, lit_value);
ecma_free_value (JERRY_CONTEXT (error_value));
}
if (value_index >= register_end)
@ -1165,6 +1145,28 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
*stack_top_p++ = ecma_make_object_value (obj_p);
continue;
}
case VM_OC_PUSH_NAMED_FUNC_EXPR:
{
ecma_object_t *func_p = ecma_get_object_from_value (left_value);
JERRY_ASSERT (ecma_get_object_type (func_p) == ECMA_OBJECT_TYPE_FUNCTION);
ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_p;
JERRY_ASSERT (frame_ctx_p->lex_env_p == ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
ext_func_p->u.function.scope_cp));
ecma_object_t *name_lex_env = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p);
ecma_op_create_immutable_binding (name_lex_env, ecma_get_string_from_value (right_value), left_value);
ECMA_SET_INTERNAL_VALUE_POINTER (ext_func_p->u.function.scope_cp, name_lex_env);
ecma_free_value (right_value);
ecma_deref_object (name_lex_env);
*stack_top_p++ = left_value;
continue;
}
#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
case VM_OC_SET_COMPUTED_PROPERTY:
{

View File

@ -120,6 +120,7 @@ typedef enum
VM_OC_PUSH_LIT_POS_BYTE, /**< push literal and number between 1 and 256 */
VM_OC_PUSH_LIT_NEG_BYTE, /**< push literal and number between -1 and -256 */
VM_OC_PUSH_OBJECT, /**< push object */
VM_OC_PUSH_NAMED_FUNC_EXPR, /**< push named function expression */
VM_OC_SET_PROPERTY, /**< set property */
#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */

View File

@ -0,0 +1,51 @@
// 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.
var expr = "Dummy value";
var f = function expr() {
assert(expr === f);
expr = 6;
assert(expr === f);
assert(!(delete expr));
assert(expr === f);
}
f();
f = function expr() {
assert(expr === undefined);
var expr = 6;
assert(expr === 6);
}
f();
var f = function expr() {
assert(expr === f);
eval("var expr = 8");
assert(expr === 8);
}
f();
var f = function expr(i) {
assert(expr === f);
if (i > 0) {
expr(i - 1);
}
}
f(10);