mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Implement for[in/of]-let construct. (#3294)
This patch implements let/const support for all "for" statements. It includes an algorithm for cloning declarative lexical environments. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
parent
58f71e6ffa
commit
da69589f05
@ -319,6 +319,76 @@ ecma_get_lex_env_binding_object (const ecma_object_t *object_p) /**< object-boun
|
||||
return ECMA_GET_NON_NULL_POINTER (ecma_object_t, object_p->u1.bound_object_cp);
|
||||
} /* ecma_get_lex_env_binding_object */
|
||||
|
||||
/**
|
||||
* Create a new lexical environment with the same property list as the passed lexical environment
|
||||
*
|
||||
* @return pointer to the newly created lexical environment
|
||||
*/
|
||||
ecma_object_t *
|
||||
ecma_clone_decl_lexical_environment (ecma_object_t *lex_env_p, /**< declarative lexical environment */
|
||||
bool copy_values) /**< copy property values as well */
|
||||
{
|
||||
JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE);
|
||||
JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
|
||||
|
||||
ecma_object_t *outer_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
|
||||
ecma_object_t *new_lex_env_p = ecma_create_decl_lex_env (outer_lex_env_p);
|
||||
|
||||
jmem_cpointer_t prop_iter_cp = lex_env_p->u1.property_list_cp;
|
||||
JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL);
|
||||
|
||||
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t,
|
||||
prop_iter_cp);
|
||||
if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
|
||||
{
|
||||
prop_iter_cp = prop_iter_p->next_property_cp;
|
||||
}
|
||||
|
||||
JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL);
|
||||
|
||||
do
|
||||
{
|
||||
prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
|
||||
|
||||
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
|
||||
|
||||
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
|
||||
|
||||
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
|
||||
{
|
||||
if (prop_iter_p->types[i] != ECMA_PROPERTY_TYPE_DELETED)
|
||||
{
|
||||
JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (prop_iter_p->types[i]) == ECMA_PROPERTY_TYPE_NAMEDDATA);
|
||||
|
||||
uint8_t prop_attributes = (uint8_t) (prop_iter_p->types[i] & ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
|
||||
ecma_string_t *name_p = ecma_string_from_property_name (prop_iter_p->types[i], prop_pair_p->names_cp[i]);
|
||||
|
||||
ecma_property_value_t *property_value_p;
|
||||
property_value_p = ecma_create_named_data_property (new_lex_env_p, name_p, prop_attributes, NULL);
|
||||
|
||||
ecma_deref_ecma_string (name_p);
|
||||
|
||||
JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED);
|
||||
|
||||
if (copy_values)
|
||||
{
|
||||
property_value_p->value = ecma_copy_value_if_not_object (prop_pair_p->values[i].value);
|
||||
}
|
||||
else
|
||||
{
|
||||
property_value_p->value = ECMA_VALUE_UNINITIALIZED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prop_iter_cp = prop_iter_p->next_property_cp;
|
||||
}
|
||||
while (prop_iter_cp != JMEM_CP_NULL);
|
||||
|
||||
ecma_deref_object (lex_env_p);
|
||||
return new_lex_env_p;
|
||||
} /* ecma_clone_decl_lexical_environment */
|
||||
|
||||
/**
|
||||
* Create a property in an object and link it into
|
||||
* the object's properties' linked-list (at start of the list).
|
||||
|
||||
@ -371,6 +371,7 @@ uint8_t ecma_get_object_builtin_id (ecma_object_t *object_p);
|
||||
ecma_lexical_environment_type_t JERRY_ATTR_PURE ecma_get_lex_env_type (const ecma_object_t *object_p);
|
||||
ecma_object_t JERRY_ATTR_PURE *ecma_get_lex_env_outer_reference (const ecma_object_t *object_p);
|
||||
ecma_object_t JERRY_ATTR_PURE *ecma_get_lex_env_binding_object (const ecma_object_t *object_p);
|
||||
ecma_object_t *ecma_clone_decl_lexical_environment (ecma_object_t *lex_env_p, bool copy_values);
|
||||
|
||||
ecma_property_value_t *
|
||||
ecma_create_named_data_property (ecma_object_t *object_p, ecma_string_t *name_p, uint8_t prop_attributes,
|
||||
|
||||
@ -544,6 +544,10 @@
|
||||
VM_OC_PUSH_LIT_POS_BYTE | VM_OC_GET_LITERAL) \
|
||||
CBC_OPCODE (CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE, CBC_HAS_LITERAL_ARG | CBC_HAS_BYTE_ARG, 2, \
|
||||
VM_OC_PUSH_LIT_NEG_BYTE | VM_OC_GET_LITERAL) \
|
||||
CBC_OPCODE (CBC_EXT_CLONE_CONTEXT, CBC_NO_FLAG, 0, \
|
||||
VM_OC_CLONE_CONTEXT) \
|
||||
CBC_OPCODE (CBC_EXT_CLONE_FULL_CONTEXT, CBC_NO_FLAG, 0, \
|
||||
VM_OC_CLONE_CONTEXT) \
|
||||
CBC_OPCODE (CBC_EXT_RESOURCE_NAME, CBC_NO_FLAG, 0, \
|
||||
VM_OC_RESOURCE_NAME) \
|
||||
CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \
|
||||
|
||||
@ -58,6 +58,7 @@ typedef enum
|
||||
PARSER_STATEMENT_START,
|
||||
PARSER_STATEMENT_BLOCK,
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
PARSER_STATEMENT_BLOCK_SCOPE,
|
||||
PARSER_STATEMENT_BLOCK_CONTEXT,
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
PARSER_STATEMENT_LABEL,
|
||||
@ -79,6 +80,7 @@ typedef enum
|
||||
PARSER_STATEMENT_FOR_IN,
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
PARSER_STATEMENT_FOR_OF,
|
||||
PARSER_STATEMENT_FOR_CONTEXT,
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
PARSER_STATEMENT_WITH,
|
||||
PARSER_STATEMENT_TRY,
|
||||
@ -234,14 +236,13 @@ parser_statement_length (uint8_t type) /**< type of statement */
|
||||
{
|
||||
static const uint8_t statement_lengths[] =
|
||||
{
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/* PARSER_STATEMENT_BLOCK */
|
||||
1,
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/* PARSER_STATEMENT_BLOCK_SCOPE */
|
||||
(uint8_t) (sizeof (parser_block_statement_t) + 1),
|
||||
/* PARSER_STATEMENT_BLOCK_CONTEXT */
|
||||
(uint8_t) (sizeof (parser_block_statement_t) + sizeof (parser_block_context_t) + 1),
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
/* PARSER_STATEMENT_BLOCK */
|
||||
1,
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
/* PARSER_STATEMENT_LABEL */
|
||||
(uint8_t) (sizeof (parser_label_statement_t) + 1),
|
||||
@ -268,6 +269,8 @@ parser_statement_length (uint8_t type) /**< type of statement */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/* PARSER_STATEMENT_FOR_OF */
|
||||
(uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
||||
/* PARSER_STATEMENT_FOR_CONTEXT */
|
||||
1,
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
/* PARSER_STATEMENT_WITH */
|
||||
(uint8_t) (sizeof (parser_with_statement_t) + 1),
|
||||
@ -342,6 +345,85 @@ parser_parse_enclosed_expr (parser_context_t *context_p) /**< context */
|
||||
lexer_next_token (context_p);
|
||||
} /* parser_parse_enclosed_expr */
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
|
||||
/**
|
||||
* Create a block context.
|
||||
*
|
||||
* @return true - when a context is created, false - otherwise
|
||||
*/
|
||||
static bool
|
||||
parser_push_block_context (parser_context_t *context_p) /**< context */
|
||||
{
|
||||
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK);
|
||||
|
||||
parser_block_statement_t block_statement;
|
||||
block_statement.scope_stack_top = context_p->scope_stack_top;
|
||||
block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top;
|
||||
|
||||
bool is_context_needed = false;
|
||||
|
||||
if (scanner_is_context_needed (context_p))
|
||||
{
|
||||
parser_block_context_t block_context;
|
||||
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_emit_cbc_forward_branch (context_p,
|
||||
CBC_BLOCK_CREATE_CONTEXT,
|
||||
&block_context.branch);
|
||||
parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t));
|
||||
is_context_needed = true;
|
||||
}
|
||||
|
||||
scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
|
||||
parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t));
|
||||
parser_stack_push_uint8 (context_p, (is_context_needed ? PARSER_STATEMENT_BLOCK_CONTEXT
|
||||
: PARSER_STATEMENT_BLOCK_SCOPE));
|
||||
|
||||
return is_context_needed;
|
||||
} /* parser_push_block_context */
|
||||
|
||||
/**
|
||||
* Pop block context.
|
||||
*/
|
||||
static void
|
||||
parser_pop_block_context (parser_context_t *context_p) /**< context */
|
||||
{
|
||||
JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE
|
||||
|| context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT);
|
||||
|
||||
uint8_t type = context_p->stack_top_uint8;
|
||||
|
||||
parser_block_statement_t block_statement;
|
||||
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t));
|
||||
|
||||
context_p->scope_stack_top = block_statement.scope_stack_top;
|
||||
context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top;
|
||||
|
||||
if (type == PARSER_STATEMENT_BLOCK_SCOPE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_block_context_t block_context;
|
||||
parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t));
|
||||
|
||||
parser_emit_cbc (context_p, CBC_CONTEXT_END);
|
||||
parser_set_branch_to_current_position (context_p, &block_context.branch);
|
||||
} /* parser_pop_block_context */
|
||||
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/**
|
||||
* Parse var statement.
|
||||
*/
|
||||
@ -980,18 +1062,33 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
||||
|| context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_OF);
|
||||
|
||||
bool is_for_in = (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN);
|
||||
|
||||
scanner_get_location (&start_location, context_p);
|
||||
lexer_next_token (context_p);
|
||||
|
||||
bool is_let_const = (context_p->token.type == LEXER_KEYW_LET || context_p->token.type == LEXER_KEYW_CONST);
|
||||
const uint8_t *source_p = context_p->source_p;
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN);
|
||||
|
||||
bool is_for_in = true;
|
||||
scanner_get_location (&start_location, context_p);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
scanner_get_location (&start_location, context_p);
|
||||
scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location);
|
||||
/* The length of both 'in' and 'of' is two. */
|
||||
const uint8_t *source_end_p = context_p->source_p - 2;
|
||||
|
||||
scanner_release_next (context_p, sizeof (scanner_location_info_t));
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (is_let_const && (context_p->next_scanner_info_p->source_p == source_p))
|
||||
{
|
||||
is_let_const = parser_push_block_context (context_p);
|
||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_CONTEXT);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
scanner_seek (context_p);
|
||||
lexer_next_token (context_p);
|
||||
parser_parse_expression (context_p, PARSE_EXPR);
|
||||
@ -1015,6 +1112,13 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
||||
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
||||
for_in_of_statement.start_offset = context_p->byte_code_size;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (is_let_const)
|
||||
{
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_CONTEXT);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/* The expression parser must not read the 'in' or 'of' tokens. */
|
||||
scanner_get_location (&end_location, context_p);
|
||||
scanner_set_location (context_p, &start_location);
|
||||
@ -1024,52 +1128,72 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
||||
scanner_seek (context_p);
|
||||
lexer_next_token (context_p);
|
||||
|
||||
if (context_p->token.type == LEXER_KEYW_VAR)
|
||||
switch (context_p->token.type)
|
||||
{
|
||||
uint16_t literal_index;
|
||||
|
||||
lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL);
|
||||
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
||||
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
||||
|
||||
literal_index = context_p->lit_object.index;
|
||||
|
||||
lexer_next_token (context_p);
|
||||
|
||||
if (context_p->token.type == LEXER_ASSIGN)
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
case LEXER_KEYW_LET:
|
||||
case LEXER_KEYW_CONST:
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
case LEXER_KEYW_VAR:
|
||||
{
|
||||
parser_branch_t branch;
|
||||
uint16_t literal_index;
|
||||
|
||||
lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL);
|
||||
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
||||
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
||||
|
||||
literal_index = context_p->lit_object.index;
|
||||
|
||||
/* Initialiser is never executed. */
|
||||
parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &branch);
|
||||
lexer_next_token (context_p);
|
||||
parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA);
|
||||
parser_set_branch_to_current_position (context_p, &branch);
|
||||
|
||||
if (context_p->token.type == LEXER_ASSIGN)
|
||||
{
|
||||
parser_branch_t branch;
|
||||
|
||||
/* Initialiser is never executed. */
|
||||
parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &branch);
|
||||
lexer_next_token (context_p);
|
||||
parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA);
|
||||
parser_set_branch_to_current_position (context_p, &branch);
|
||||
}
|
||||
|
||||
parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT
|
||||
: CBC_EXT_FOR_OF_GET_NEXT);
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (literal_index >= PARSER_REGISTER_START)
|
||||
{
|
||||
is_let_const = false;
|
||||
}
|
||||
|
||||
parser_emit_cbc_literal (context_p,
|
||||
is_let_const ? CBC_ASSIGN_LET_CONST : CBC_ASSIGN_SET_IDENT,
|
||||
literal_index);
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
uint16_t opcode;
|
||||
|
||||
parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT
|
||||
: CBC_EXT_FOR_OF_GET_NEXT);
|
||||
parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t opcode;
|
||||
parser_parse_expression (context_p, PARSE_EXPR_LEFT_HAND_SIDE);
|
||||
|
||||
parser_parse_expression (context_p, PARSE_EXPR_LEFT_HAND_SIDE);
|
||||
opcode = context_p->last_cbc_opcode;
|
||||
|
||||
opcode = context_p->last_cbc_opcode;
|
||||
/* The CBC_EXT_FOR_IN_CREATE_CONTEXT flushed the opcode combiner. */
|
||||
JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS
|
||||
&& opcode != CBC_PUSH_THREE_LITERALS);
|
||||
|
||||
/* The CBC_EXT_FOR_IN_CREATE_CONTEXT flushed the opcode combiner. */
|
||||
JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS
|
||||
&& opcode != CBC_PUSH_THREE_LITERALS);
|
||||
opcode = parser_check_left_hand_side_expression (context_p, opcode);
|
||||
|
||||
opcode = parser_check_left_hand_side_expression (context_p, opcode);
|
||||
parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT
|
||||
: CBC_EXT_FOR_OF_GET_NEXT);
|
||||
parser_flush_cbc (context_p);
|
||||
|
||||
parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT
|
||||
: CBC_EXT_FOR_OF_GET_NEXT);
|
||||
parser_flush_cbc (context_p);
|
||||
|
||||
context_p->last_cbc_opcode = opcode;
|
||||
context_p->last_cbc_opcode = opcode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (context_p->token.type != LEXER_EOS)
|
||||
@ -1104,13 +1228,30 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
||||
|
||||
if (context_p->token.type != LEXER_SEMICOLON)
|
||||
{
|
||||
if (context_p->token.type == LEXER_KEYW_VAR)
|
||||
switch (context_p->token.type)
|
||||
{
|
||||
parser_parse_var_statement (context_p);
|
||||
}
|
||||
else
|
||||
{
|
||||
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
case LEXER_KEYW_LET:
|
||||
case LEXER_KEYW_CONST:
|
||||
{
|
||||
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
||||
{
|
||||
parser_push_block_context (context_p);
|
||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_CONTEXT);
|
||||
}
|
||||
/* FALLTHRU */
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
case LEXER_KEYW_VAR:
|
||||
{
|
||||
parser_parse_var_statement (context_p);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (context_p->token.type != LEXER_SEMICOLON)
|
||||
@ -1194,6 +1335,24 @@ parser_parse_for_statement_end (parser_context_t *context_p) /**< context */
|
||||
parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t));
|
||||
parser_stack_iterator_read (&iterator, &for_statement, sizeof (parser_for_statement_t));
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
bool has_block_context = false;
|
||||
uint8_t next_statement_type;
|
||||
|
||||
parser_stack_iterator_skip (&iterator, sizeof (parser_for_statement_t));
|
||||
parser_stack_iterator_read (&iterator, &next_statement_type, 1);
|
||||
if (next_statement_type == PARSER_STATEMENT_FOR_CONTEXT)
|
||||
{
|
||||
parser_stack_iterator_skip (&iterator, 1);
|
||||
parser_stack_iterator_read (&iterator, &next_statement_type, 1);
|
||||
|
||||
if (next_statement_type == PARSER_STATEMENT_BLOCK_CONTEXT)
|
||||
{
|
||||
has_block_context = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
scanner_get_location (&location, context_p);
|
||||
current_token = context_p->token;
|
||||
|
||||
@ -1203,6 +1362,13 @@ parser_parse_for_statement_end (parser_context_t *context_p) /**< context */
|
||||
|
||||
parser_set_continues_to_current_position (context_p, loop.branch_list_p);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (has_block_context)
|
||||
{
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_FULL_CONTEXT);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
||||
{
|
||||
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
||||
@ -1251,6 +1417,15 @@ parser_parse_for_statement_end (parser_context_t *context_p) /**< context */
|
||||
parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, for_statement.start_offset);
|
||||
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_CONTEXT)
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
parser_pop_block_context (context_p);
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Calling scanner_seek is unnecessary because all
|
||||
* info blocks inside the for statement should be processed. */
|
||||
scanner_set_location (context_p, &location);
|
||||
@ -1324,13 +1499,6 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context *
|
||||
parser_emit_cbc (context_p, CBC_POP);
|
||||
parser_flush_cbc (context_p);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
parser_block_statement_t block_statement;
|
||||
block_statement.scope_stack_top = context_p->scope_stack_top;
|
||||
block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top;
|
||||
parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t));
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK);
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
return;
|
||||
@ -2483,39 +2651,19 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
|
||||
case LEXER_LEFT_BRACE:
|
||||
{
|
||||
uint8_t block_type = PARSER_STATEMENT_BLOCK;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
parser_block_statement_t block_statement;
|
||||
block_statement.scope_stack_top = context_p->scope_stack_top;
|
||||
block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top;
|
||||
|
||||
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
||||
{
|
||||
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK);
|
||||
|
||||
if (scanner_is_context_needed (context_p))
|
||||
{
|
||||
parser_block_context_t block_context;
|
||||
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_emit_cbc_forward_branch (context_p,
|
||||
CBC_BLOCK_CREATE_CONTEXT,
|
||||
&block_context.branch);
|
||||
parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t));
|
||||
block_type = PARSER_STATEMENT_BLOCK_CONTEXT;
|
||||
}
|
||||
|
||||
scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
|
||||
parser_push_block_context (context_p);
|
||||
}
|
||||
|
||||
parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t));
|
||||
else
|
||||
{
|
||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK);
|
||||
}
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
parser_stack_push_uint8 (context_p, block_type);
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
lexer_next_token (context_p);
|
||||
continue;
|
||||
@ -2810,38 +2958,14 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK)
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
parser_block_statement_t block_statement;
|
||||
|
||||
parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t));
|
||||
context_p->scope_stack_top = block_statement.scope_stack_top;
|
||||
context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
lexer_next_token (context_p);
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
else if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT)
|
||||
else if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE
|
||||
|| context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT)
|
||||
{
|
||||
parser_block_statement_t block_statement;
|
||||
parser_block_context_t block_context;
|
||||
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t));
|
||||
parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t));
|
||||
|
||||
context_p->scope_stack_top = block_statement.scope_stack_top;
|
||||
context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top;
|
||||
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_emit_cbc (context_p, CBC_CONTEXT_END);
|
||||
parser_set_branch_to_current_position (context_p, &block_context.branch);
|
||||
parser_pop_block_context (context_p);
|
||||
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
lexer_next_token (context_p);
|
||||
@ -3038,6 +3162,15 @@ consume_last_statement:
|
||||
|
||||
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
||||
parser_set_branch_to_current_position (context_p, &for_in_of_statement.branch);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_CONTEXT)
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
parser_pop_block_context (context_p);
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -80,6 +80,10 @@ typedef enum
|
||||
/* The SCANNER_IS_FOR_START macro needs to be updated when the following constants are reordered. */
|
||||
SCAN_STACK_VAR, /**< var statement */
|
||||
SCAN_STACK_FOR_VAR_START, /**< start of "for" iterator with var statement */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
SCAN_STACK_FOR_LET_START, /**< start of "for" iterator with let statement */
|
||||
SCAN_STACK_FOR_CONST_START, /**< start of "for" iterator with const statement */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
SCAN_STACK_FOR_START, /**< start of "for" iterator */
|
||||
SCAN_STACK_FOR_CONDITION, /**< condition part of "for" iterator */
|
||||
SCAN_STACK_FOR_EXPRESSION, /**< expression part of "for" iterator */
|
||||
@ -93,6 +97,7 @@ typedef enum
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
SCAN_STACK_COMPUTED_PROPERTY, /**< computed property name */
|
||||
SCAN_STACK_TEMPLATE_STRING, /**< template string */
|
||||
SCAN_STACK_FOR_BLOCK_END, /**< end of "for" statement with let/const declaration */
|
||||
SCAN_STACK_ARROW_ARGUMENTS, /**< might be arguments of an arrow function */
|
||||
SCAN_STACK_ARROW_EXPRESSION, /**< expression body of an arrow function */
|
||||
SCAN_STACK_CLASS_STATEMENT, /**< class statement */
|
||||
@ -517,6 +522,10 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
|
||||
case SCAN_STACK_CONST:
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
case SCAN_STACK_FOR_VAR_START:
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
case SCAN_STACK_FOR_LET_START:
|
||||
case SCAN_STACK_FOR_CONST_START:
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
{
|
||||
scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT;
|
||||
return SCAN_NEXT_TOKEN;
|
||||
@ -540,7 +549,9 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
|
||||
}
|
||||
|
||||
JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_LET
|
||||
|| context_p->stack_top_uint8 == SCAN_STACK_CONST);
|
||||
|| context_p->stack_top_uint8 == SCAN_STACK_CONST
|
||||
|| context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START
|
||||
|| context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START);
|
||||
|
||||
scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT;
|
||||
return SCAN_NEXT_TOKEN;
|
||||
@ -670,6 +681,13 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
|
||||
let_const_literal.literal_p->type |= SCANNER_LITERAL_NO_REG;
|
||||
}
|
||||
|
||||
if (context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START
|
||||
|| context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START)
|
||||
{
|
||||
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
|
||||
return SCAN_KEEP_TOKEN;
|
||||
}
|
||||
|
||||
JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_LET
|
||||
|| context_p->stack_top_uint8 == SCAN_STACK_CONST);
|
||||
/* FALLTHRU */
|
||||
@ -689,6 +707,10 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
|
||||
return SCAN_KEEP_TOKEN;
|
||||
}
|
||||
case SCAN_STACK_FOR_VAR_START:
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
case SCAN_STACK_FOR_LET_START:
|
||||
case SCAN_STACK_FOR_CONST_START:
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
case SCAN_STACK_FOR_START:
|
||||
{
|
||||
if (type == LEXER_KEYW_IN || SCANNER_IDENTIFIER_IS_OF ())
|
||||
@ -704,6 +726,11 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
|
||||
sizeof (scanner_location_info_t));
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
location_info->info.type = (type == LEXER_KEYW_IN) ? SCANNER_TYPE_FOR_IN : SCANNER_TYPE_FOR_OF;
|
||||
|
||||
if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START)
|
||||
{
|
||||
parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_BLOCK_END);
|
||||
}
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
location_info->info.type = SCANNER_TYPE_FOR_IN;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
@ -725,6 +752,13 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
parser_stack_pop (context_p, NULL, sizeof (scanner_for_statement_t));
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START)
|
||||
{
|
||||
parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_BLOCK_END);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
for_statement.u.source_p = context_p->source_p;
|
||||
parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t));
|
||||
parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_CONDITION);
|
||||
@ -1147,6 +1181,21 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */
|
||||
stack_mode = SCAN_STACK_FOR_VAR_START;
|
||||
break;
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
case LEXER_KEYW_LET:
|
||||
case LEXER_KEYW_CONST:
|
||||
{
|
||||
scanner_literal_pool_t *literal_pool_p;
|
||||
literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_BLOCK);
|
||||
literal_pool_p->source_p = context_p->source_p;
|
||||
|
||||
scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT;
|
||||
|
||||
stack_mode = ((context_p->token.type == LEXER_KEYW_LET) ? SCAN_STACK_FOR_LET_START
|
||||
: SCAN_STACK_FOR_CONST_START);
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
}
|
||||
|
||||
parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t));
|
||||
@ -1903,6 +1952,14 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */
|
||||
terminator_found = true;
|
||||
continue;
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
case SCAN_STACK_FOR_BLOCK_END:
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
scanner_pop_literal_pool (context_p, scanner_context_p);
|
||||
continue;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
default:
|
||||
{
|
||||
JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_TRY_STATEMENT
|
||||
@ -2236,16 +2293,17 @@ scanner_scan_all (parser_context_t *context_p, /**< context */
|
||||
lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_CONST)
|
||||
if (stack_top != SCAN_STACK_VAR && stack_top != SCAN_STACK_FOR_VAR_START)
|
||||
{
|
||||
scanner_detect_invalid_let (context_p, literal_p);
|
||||
|
||||
if (stack_top == SCAN_STACK_LET)
|
||||
if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_FOR_LET_START)
|
||||
{
|
||||
literal_p->type |= SCANNER_LITERAL_IS_LET;
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_ASSERT (stack_top == SCAN_STACK_CONST || stack_top == SCAN_STACK_FOR_CONST_START);
|
||||
literal_p->type |= SCANNER_LITERAL_IS_CONST;
|
||||
}
|
||||
|
||||
|
||||
@ -1335,6 +1335,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case VM_OC_CLONE_CONTEXT:
|
||||
{
|
||||
JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE);
|
||||
|
||||
bool copy_values = (byte_code_start_p[1] == CBC_EXT_CLONE_FULL_CONTEXT);
|
||||
frame_ctx_p->lex_env_p = ecma_clone_decl_lexical_environment (frame_ctx_p->lex_env_p, copy_values);
|
||||
continue;
|
||||
}
|
||||
case VM_OC_SET_COMPUTED_PROPERTY:
|
||||
{
|
||||
/* Swap values. */
|
||||
@ -3025,6 +3033,13 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
stack_top_p[-3] = 0;
|
||||
stack_top_p[-4] = expr_obj_value;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT)
|
||||
{
|
||||
/* No need to duplicate the first context. */
|
||||
byte_code_p += 2;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
continue;
|
||||
}
|
||||
case VM_OC_FOR_IN_GET_NEXT:
|
||||
@ -3124,6 +3139,11 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
stack_top_p[-2] = iterator_step;
|
||||
stack_top_p[-3] = iterator;
|
||||
|
||||
if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT)
|
||||
{
|
||||
/* No need to duplicate the first context. */
|
||||
byte_code_p += 2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case VM_OC_FOR_OF_GET_NEXT:
|
||||
|
||||
@ -226,6 +226,7 @@ typedef enum
|
||||
#endif /* ENABLED (JERRY_LINE_INFO) */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */
|
||||
VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */
|
||||
VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */
|
||||
|
||||
VM_OC_FOR_OF_CREATE_CONTEXT, /**< for of create context */
|
||||
@ -265,6 +266,7 @@ typedef enum
|
||||
#endif /* !ENABLED (JERRY_LINE_INFO) */
|
||||
#if !ENABLED (JERRY_ES2015)
|
||||
VM_OC_ASSIGN_LET_CONST = VM_OC_NONE, /**< assign values to let/const declarations */
|
||||
VM_OC_CLONE_CONTEXT = VM_OC_NONE, /**< clone lexical environment with let/const declarations */
|
||||
VM_OC_SET_COMPUTED_PROPERTY = VM_OC_NONE, /**< set computed property is unused */
|
||||
VM_OC_BLOCK_CREATE_CONTEXT = VM_OC_NONE, /**< create context for blocks enclosed in braces */
|
||||
|
||||
|
||||
105
tests/jerry/es2015/for-let.js
Normal file
105
tests/jerry/es2015/for-let.js
Normal file
@ -0,0 +1,105 @@
|
||||
// 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 arr = [];
|
||||
for (let i = 0, j = 0; i < 10; j++)
|
||||
{
|
||||
arr[i] = function() { return i + j; }
|
||||
i++;
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++)
|
||||
assert(arr[i]() === (i * 2 + 1));
|
||||
|
||||
var j = 0, k = 0;
|
||||
for (let i = 0; j = i, i < 10; i++)
|
||||
{
|
||||
let i = -3;
|
||||
assert(i === -3);
|
||||
assert(j === k);
|
||||
k++;
|
||||
}
|
||||
|
||||
var j = 0, k = 0;
|
||||
for (let i = 0; eval("j = i"), i < 10; i++)
|
||||
{
|
||||
let i = -3;
|
||||
assert(i === -3);
|
||||
assert(j === k);
|
||||
k++;
|
||||
}
|
||||
|
||||
var arr = [];
|
||||
for (let i in { x:1, y:1, z:1 })
|
||||
{
|
||||
let str = "P";
|
||||
arr.push(function () { return str + i; });
|
||||
}
|
||||
|
||||
assert(arr[0]() === "Px");
|
||||
assert(arr[1]() === "Py");
|
||||
assert(arr[2]() === "Pz");
|
||||
|
||||
try {
|
||||
for (let i in (function() { return i; })()) {}
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof ReferenceError);
|
||||
}
|
||||
|
||||
try {
|
||||
for (let i = 0, j = 0; i < 5; i++, j++)
|
||||
{
|
||||
if (i === 3)
|
||||
{
|
||||
eval("throw -42")
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e === -42);
|
||||
}
|
||||
|
||||
exit: {
|
||||
for (let i = 0, j = 0; i < 5; i++, j++)
|
||||
{
|
||||
if (eval("i === 3")) {
|
||||
assert(i === 3);
|
||||
break exit;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
var f = null, g = null, h = null;
|
||||
|
||||
for (let i = 0;
|
||||
f = function() { return i }, i < 1;
|
||||
i++, g = function() { return i })
|
||||
{
|
||||
h = function() { return i };
|
||||
}
|
||||
assert(f() === 1);
|
||||
assert(g() === 1);
|
||||
assert(h() === 0);
|
||||
|
||||
var arr = [];
|
||||
for (const i in { aa:4, bb:5, cc:6 })
|
||||
{
|
||||
arr.push(function () { return i });
|
||||
}
|
||||
|
||||
assert(arr[0]() === "aa");
|
||||
assert(arr[1]() === "bb");
|
||||
assert(arr[2]() === "cc");
|
||||
Loading…
x
Reference in New Issue
Block a user