mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Implement the for of statement (#2871)
JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
parent
b6fc4e13ae
commit
343e80053b
@ -110,6 +110,10 @@
|
||||
# define JERRY_ES2015_CLASS JERRY_ES2015
|
||||
#endif /* !defined (JERRY_ES2015_CLASS) */
|
||||
|
||||
#ifndef JERRY_ES2015_FOR_OF
|
||||
# define JERRY_ES2015_FOR_OF JERRY_ES2015
|
||||
#endif /* !defined (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
#ifndef JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER
|
||||
# define JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER JERRY_ES2015
|
||||
#endif /* !defined (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) */
|
||||
@ -298,6 +302,10 @@
|
||||
|| ((JERRY_ES2015_CLASS != 0) && (JERRY_ES2015_CLASS != 1))
|
||||
# error "Invalid value for JERRY_ES2015_CLASS macro."
|
||||
#endif
|
||||
#if !defined (JERRY_ES2015_FOR_OF) \
|
||||
|| ((JERRY_ES2015_FOR_OF != 0) && (JERRY_ES2015_FOR_OF != 1))
|
||||
# error "Invalid value for JERRY_ES2015_FOR_OF macro."
|
||||
#endif
|
||||
#if !defined (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) \
|
||||
|| ((JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER != 0) && (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER != 1))
|
||||
# error "Invalid value for JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER macro."
|
||||
|
||||
@ -30,7 +30,7 @@ extern "C"
|
||||
/**
|
||||
* Jerry snapshot format version.
|
||||
*/
|
||||
#define JERRY_SNAPSHOT_VERSION (22u)
|
||||
#define JERRY_SNAPSHOT_VERSION (23u)
|
||||
|
||||
/**
|
||||
* Flags for jerry_generate_snapshot and jerry_generate_function_snapshot.
|
||||
|
||||
@ -188,6 +188,8 @@
|
||||
|
||||
/* Stack consumption of opcodes with context. */
|
||||
|
||||
/* PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION must be <= 3 */
|
||||
#define PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION 3
|
||||
/* PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION must be <= 4 */
|
||||
#define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 4
|
||||
/* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */
|
||||
@ -492,6 +494,14 @@
|
||||
VM_OC_SET_GETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \
|
||||
CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT, 0, \
|
||||
VM_OC_FOR_IN_HAS_NEXT) \
|
||||
CBC_OPCODE (CBC_EXT_FOR_OF_GET_NEXT, CBC_NO_FLAG, 1, \
|
||||
VM_OC_FOR_OF_GET_NEXT | VM_OC_PUT_STACK) \
|
||||
CBC_FORWARD_BRANCH (CBC_EXT_FOR_OF_CREATE_CONTEXT, \
|
||||
-1 + PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_OF_CREATE_CONTEXT) \
|
||||
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_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT, 0, \
|
||||
VM_OC_FOR_OF_HAS_NEXT) \
|
||||
CBC_OPCODE (CBC_EXT_SET_SETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \
|
||||
VM_OC_SET_SETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \
|
||||
CBC_FORWARD_BRANCH (CBC_EXT_TRY_CREATE_CONTEXT, PARSER_TRY_CONTEXT_STACK_ALLOCATION, \
|
||||
@ -510,8 +520,6 @@
|
||||
-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, \
|
||||
|
||||
@ -153,6 +153,10 @@ typedef enum
|
||||
LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */
|
||||
LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */
|
||||
LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
LEXER_FOR_IN_OF, /**< special value during for in/of statmenet scanning */
|
||||
LEXER_LITERAL_OF, /**< 'of' literal */
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
#if !ENABLED (JERRY_ES2015)
|
||||
/* Future reserved words: these keywords
|
||||
|
||||
@ -675,7 +675,13 @@ parser_scan_until (parser_context_t *context_p, /**< context */
|
||||
{
|
||||
lexer_next_token (context_p);
|
||||
|
||||
if (end_type == LEXER_KEYW_IN)
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
lexer_token_type_t for_in_of_token = LEXER_FOR_IN_OF;
|
||||
#else /* !ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
lexer_token_type_t for_in_of_token = LEXER_KEYW_IN;
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
if (end_type == for_in_of_token)
|
||||
{
|
||||
end_type_b = LEXER_SEMICOLON;
|
||||
if (context_p->token.type == LEXER_KEYW_VAR)
|
||||
@ -697,11 +703,31 @@ parser_scan_until (parser_context_t *context_p, /**< context */
|
||||
parser_raise_error (context_p, PARSER_ERR_EXPRESSION_EXPECTED);
|
||||
}
|
||||
|
||||
if (stack_top == SCAN_STACK_HEAD
|
||||
&& (type == end_type || type == end_type_b))
|
||||
if (stack_top == SCAN_STACK_HEAD)
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
return;
|
||||
if (type == end_type || type == end_type_b)
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
return;
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
if (end_type == LEXER_FOR_IN_OF)
|
||||
{
|
||||
if (type == LEXER_KEYW_IN)
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
context_p->token.type = LEXER_KEYW_IN;
|
||||
return;
|
||||
}
|
||||
else if (type == LEXER_LITERAL && lexer_compare_raw_identifier_to_current (context_p, "of", 2))
|
||||
{
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
context_p->token.type = LEXER_LITERAL_OF;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
|
||||
@ -21,6 +21,13 @@
|
||||
#include "ecma-helpers.h"
|
||||
#include "lit-char-helpers.h"
|
||||
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
#if !ENABLED (JERRY_ES2015_BUILTIN_ITERATOR)
|
||||
#error "For of support requires ES2015 iterator support"
|
||||
#endif /* !ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) */
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
|
||||
/** \addtogroup parser Parser
|
||||
* @{
|
||||
*
|
||||
@ -71,6 +78,9 @@ typedef enum
|
||||
* Break and continue uses another instruction form
|
||||
* when crosses their borders. */
|
||||
PARSER_STATEMENT_FOR_IN,
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
PARSER_STATEMENT_FOR_OF,
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
PARSER_STATEMENT_WITH,
|
||||
PARSER_STATEMENT_TRY,
|
||||
} parser_statement_type_t;
|
||||
@ -147,6 +157,17 @@ typedef struct
|
||||
uint32_t start_offset; /**< start byte code offset */
|
||||
} parser_for_in_statement_t;
|
||||
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
/**
|
||||
* For-of statement.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
parser_branch_t branch; /**< branch to the end */
|
||||
uint32_t start_offset; /**< start byte code offset */
|
||||
} parser_for_of_statement_t;
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
/**
|
||||
* With statement.
|
||||
*/
|
||||
@ -183,7 +204,7 @@ typedef struct
|
||||
static inline size_t
|
||||
parser_statement_length (uint8_t type) /**< type of statement */
|
||||
{
|
||||
static const uint8_t statement_lengths[12] =
|
||||
static const uint8_t statement_lengths[] =
|
||||
{
|
||||
/* PARSER_STATEMENT_BLOCK */
|
||||
1,
|
||||
@ -205,6 +226,10 @@ parser_statement_length (uint8_t type) /**< type of statement */
|
||||
(uint8_t) (sizeof (parser_for_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
||||
/* PARSER_STATEMENT_FOR_IN */
|
||||
(uint8_t) (sizeof (parser_for_in_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
/* PARSER_STATEMENT_FOR_OF */
|
||||
(uint8_t) (sizeof (parser_for_of_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
/* PARSER_STATEMENT_WITH */
|
||||
(uint8_t) (sizeof (parser_with_statement_t) + 1),
|
||||
/* PARSER_STATEMENT_TRY */
|
||||
@ -212,7 +237,6 @@ parser_statement_length (uint8_t type) /**< type of statement */
|
||||
};
|
||||
|
||||
JERRY_ASSERT (type >= PARSER_STATEMENT_BLOCK && type <= PARSER_STATEMENT_TRY);
|
||||
JERRY_ASSERT (PARSER_STATEMENT_TRY - PARSER_STATEMENT_BLOCK == 11);
|
||||
|
||||
return statement_lengths[type - PARSER_STATEMENT_BLOCK];
|
||||
} /* parser_statement_length */
|
||||
@ -837,6 +861,52 @@ parser_parse_while_statement_end (parser_context_t *context_p) /**< context */
|
||||
context_p->token = current_token;
|
||||
} /* parser_parse_while_statement_end */
|
||||
|
||||
/**
|
||||
* Check whether the opcode is a valid LeftHandSide expression
|
||||
* and convert it back to an assignment.
|
||||
*
|
||||
* @return the compatible assignment opcode
|
||||
*/
|
||||
static uint16_t
|
||||
parser_check_left_hand_side_expression (parser_context_t *context_p, /**< context */
|
||||
uint16_t opcode) /**< opcode to check */
|
||||
{
|
||||
if (opcode == CBC_PUSH_LITERAL
|
||||
&& context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
|
||||
{
|
||||
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
||||
return CBC_ASSIGN_SET_IDENT;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP)
|
||||
{
|
||||
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
||||
return CBC_ASSIGN;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP_LITERAL)
|
||||
{
|
||||
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
||||
return CBC_ASSIGN_PROP_LITERAL;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP_LITERAL_LITERAL)
|
||||
{
|
||||
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
||||
return CBC_ASSIGN;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP_THIS_LITERAL)
|
||||
{
|
||||
context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL;
|
||||
return CBC_ASSIGN;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Invalid LeftHandSide expression. */
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
||||
return CBC_ASSIGN;
|
||||
}
|
||||
|
||||
return opcode;
|
||||
} /* parser_check_left_hand_side_expression */
|
||||
|
||||
/**
|
||||
* Parse for statement (starting part).
|
||||
*/
|
||||
@ -854,7 +924,13 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
||||
parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED);
|
||||
}
|
||||
|
||||
parser_scan_until (context_p, &start_range, LEXER_KEYW_IN);
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
lexer_token_type_t scan_token = LEXER_FOR_IN_OF;
|
||||
#else /* !ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
lexer_token_type_t scan_token = LEXER_KEYW_IN;
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
parser_scan_until (context_p, &start_range, scan_token);
|
||||
|
||||
if (context_p->token.type == LEXER_KEYW_IN)
|
||||
{
|
||||
@ -925,38 +1001,7 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
||||
JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS
|
||||
&& opcode != CBC_PUSH_THREE_LITERALS);
|
||||
|
||||
if (opcode == CBC_PUSH_LITERAL
|
||||
&& context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
|
||||
{
|
||||
opcode = CBC_ASSIGN_SET_IDENT;
|
||||
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP)
|
||||
{
|
||||
opcode = CBC_ASSIGN;
|
||||
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP_LITERAL)
|
||||
{
|
||||
opcode = CBC_ASSIGN_PROP_LITERAL;
|
||||
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP_LITERAL_LITERAL)
|
||||
{
|
||||
opcode = CBC_ASSIGN;
|
||||
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
||||
}
|
||||
else if (opcode == CBC_PUSH_PROP_THIS_LITERAL)
|
||||
{
|
||||
opcode = CBC_ASSIGN;
|
||||
context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Invalid LeftHandSide expression. */
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
||||
opcode = CBC_ASSIGN;
|
||||
}
|
||||
opcode = parser_check_left_hand_side_expression (context_p, opcode);
|
||||
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_FOR_IN_GET_NEXT);
|
||||
parser_flush_cbc (context_p);
|
||||
@ -980,6 +1025,101 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_IN);
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
else if (context_p->token.type == LEXER_LITERAL_OF)
|
||||
{
|
||||
parser_for_of_statement_t for_of_statement;
|
||||
lexer_range_t range;
|
||||
|
||||
lexer_next_token (context_p);
|
||||
parser_parse_expression (context_p, PARSE_EXPR);
|
||||
|
||||
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
||||
}
|
||||
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_emit_cbc_ext_forward_branch (context_p,
|
||||
CBC_EXT_FOR_OF_CREATE_CONTEXT,
|
||||
&for_of_statement.branch);
|
||||
|
||||
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
||||
for_of_statement.start_offset = context_p->byte_code_size;
|
||||
|
||||
parser_save_range (context_p, &range, context_p->source_end_p);
|
||||
parser_set_range (context_p, &start_range);
|
||||
lexer_next_token (context_p);
|
||||
|
||||
if (context_p->token.type == LEXER_KEYW_VAR)
|
||||
{
|
||||
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);
|
||||
|
||||
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR;
|
||||
|
||||
literal_index = context_p->lit_object.index;
|
||||
|
||||
lexer_next_token (context_p);
|
||||
|
||||
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 (context_p,
|
||||
PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA);
|
||||
parser_set_branch_to_current_position (context_p, &branch);
|
||||
}
|
||||
|
||||
parser_emit_cbc_ext (context_p, 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);
|
||||
|
||||
opcode = context_p->last_cbc_opcode;
|
||||
|
||||
/* The CBC_EXT_FOR_OF_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);
|
||||
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_FOR_OF_GET_NEXT);
|
||||
parser_flush_cbc (context_p);
|
||||
|
||||
context_p->last_cbc_opcode = opcode;
|
||||
}
|
||||
|
||||
if (context_p->token.type != LEXER_EOS)
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_OF_EXPECTED);
|
||||
}
|
||||
|
||||
parser_flush_cbc (context_p);
|
||||
parser_set_range (context_p, &range);
|
||||
lexer_next_token (context_p);
|
||||
|
||||
loop.branch_list_p = NULL;
|
||||
|
||||
parser_stack_push (context_p, &for_of_statement, sizeof (parser_for_of_statement_t));
|
||||
parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t));
|
||||
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_OF);
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
else
|
||||
{
|
||||
parser_for_statement_t for_statement;
|
||||
@ -1482,6 +1622,9 @@ parser_parse_break_statement (parser_context_t *context_p) /**< context */
|
||||
}
|
||||
|
||||
if (type == PARSER_STATEMENT_FOR_IN
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
|| type == PARSER_STATEMENT_FOR_OF
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|| type == PARSER_STATEMENT_WITH
|
||||
|| type == PARSER_STATEMENT_TRY)
|
||||
{
|
||||
@ -1523,6 +1666,9 @@ parser_parse_break_statement (parser_context_t *context_p) /**< context */
|
||||
}
|
||||
|
||||
if (type == PARSER_STATEMENT_FOR_IN
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
|| type == PARSER_STATEMENT_FOR_OF
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|| type == PARSER_STATEMENT_WITH
|
||||
|| type == PARSER_STATEMENT_TRY)
|
||||
{
|
||||
@ -1534,6 +1680,9 @@ parser_parse_break_statement (parser_context_t *context_p) /**< context */
|
||||
|| type == PARSER_STATEMENT_DO_WHILE
|
||||
|| type == PARSER_STATEMENT_WHILE
|
||||
|| type == PARSER_STATEMENT_FOR
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
|| type == PARSER_STATEMENT_FOR_OF
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|| type == PARSER_STATEMENT_FOR_IN)
|
||||
{
|
||||
parser_loop_statement_t loop;
|
||||
@ -1568,7 +1717,7 @@ parser_parse_continue_statement (parser_context_t *context_p) /**< context */
|
||||
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
|
||||
{
|
||||
parser_stack_iterator_t loop_iterator;
|
||||
bool for_in_was_seen = false;
|
||||
bool for_in_of_was_seen = false;
|
||||
|
||||
loop_iterator.current_p = NULL;
|
||||
|
||||
@ -1608,20 +1757,29 @@ parser_parse_continue_statement (parser_context_t *context_p) /**< context */
|
||||
continue;
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
bool is_for_in_of_statement = (type == PARSER_STATEMENT_FOR_IN) || (type == PARSER_STATEMENT_FOR_OF);
|
||||
#else /* !ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
bool is_for_in_of_statement = (type == PARSER_STATEMENT_FOR_IN);
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
if (type == PARSER_STATEMENT_WITH
|
||||
|| type == PARSER_STATEMENT_TRY
|
||||
|| for_in_was_seen)
|
||||
|| for_in_of_was_seen)
|
||||
{
|
||||
opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT;
|
||||
}
|
||||
else if (type == PARSER_STATEMENT_FOR_IN)
|
||||
else if (is_for_in_of_statement)
|
||||
{
|
||||
for_in_was_seen = true;
|
||||
for_in_of_was_seen = true;
|
||||
}
|
||||
|
||||
if (type == PARSER_STATEMENT_DO_WHILE
|
||||
|| type == PARSER_STATEMENT_WHILE
|
||||
|| type == PARSER_STATEMENT_FOR
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
|| type == PARSER_STATEMENT_FOR_OF
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|| type == PARSER_STATEMENT_FOR_IN)
|
||||
{
|
||||
loop_iterator = iterator;
|
||||
@ -1647,6 +1805,9 @@ parser_parse_continue_statement (parser_context_t *context_p) /**< context */
|
||||
if (type == PARSER_STATEMENT_DO_WHILE
|
||||
|| type == PARSER_STATEMENT_WHILE
|
||||
|| type == PARSER_STATEMENT_FOR
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
|| type == PARSER_STATEMENT_FOR_OF
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|| type == PARSER_STATEMENT_FOR_IN)
|
||||
{
|
||||
parser_loop_statement_t loop;
|
||||
@ -2182,6 +2343,9 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
|| context_p->stack_top_uint8 == PARSER_STATEMENT_WHILE
|
||||
|| context_p->stack_top_uint8 == PARSER_STATEMENT_FOR
|
||||
|| context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_IN
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
|| context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_OF
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|| context_p->stack_top_uint8 == PARSER_STATEMENT_WITH)
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_STATEMENT_EXPECTED);
|
||||
@ -2619,6 +2783,34 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
parser_set_branch_to_current_position (context_p, &for_in_statement.branch);
|
||||
continue;
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
case PARSER_STATEMENT_FOR_OF:
|
||||
{
|
||||
parser_for_of_statement_t for_of_statement;
|
||||
parser_loop_statement_t loop;
|
||||
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t));
|
||||
parser_stack_pop (context_p, &for_of_statement, sizeof (parser_for_of_statement_t));
|
||||
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
||||
|
||||
parser_set_continues_to_current_position (context_p, loop.branch_list_p);
|
||||
|
||||
parser_flush_cbc (context_p);
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_emit_cbc_ext_backward_branch (context_p,
|
||||
CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT,
|
||||
for_of_statement.start_offset);
|
||||
|
||||
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
||||
parser_set_branch_to_current_position (context_p, &for_of_statement.branch);
|
||||
continue;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
|
||||
case PARSER_STATEMENT_WITH:
|
||||
{
|
||||
@ -2705,6 +2897,9 @@ parser_free_jumps (parser_stack_iterator_t iterator) /**< iterator position */
|
||||
case PARSER_STATEMENT_WHILE:
|
||||
case PARSER_STATEMENT_FOR:
|
||||
case PARSER_STATEMENT_FOR_IN:
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
case PARSER_STATEMENT_FOR_OF:
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
{
|
||||
parser_loop_statement_t loop;
|
||||
|
||||
|
||||
@ -954,6 +954,12 @@ parser_error_to_string (parser_error_t error) /**< error code */
|
||||
{
|
||||
return "Expected 'in' token.";
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
case PARSER_ERR_OF_EXPECTED:
|
||||
{
|
||||
return "Expected 'of' token.";
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
case PARSER_ERR_WHILE_EXPECTED:
|
||||
{
|
||||
return "While expected for do-while loop.";
|
||||
|
||||
@ -98,6 +98,9 @@ typedef enum
|
||||
PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED, /**< colon expected for conditional expression */
|
||||
PARSER_ERR_SEMICOLON_EXPECTED, /**< semicolon expected */
|
||||
PARSER_ERR_IN_EXPECTED, /**< in keyword expected */
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
PARSER_ERR_OF_EXPECTED, /**< of keyword expected */
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
PARSER_ERR_WHILE_EXPECTED, /**< while expected for do-while loop */
|
||||
PARSER_ERR_CATCH_FINALLY_EXPECTED, /**< catch or finally expected */
|
||||
PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED, /**< array item separator expected */
|
||||
|
||||
@ -39,6 +39,7 @@ JERRY_ES2015_BUILTIN_SET=0
|
||||
JERRY_ES2015_BUILTIN_SYMBOL=0
|
||||
JERRY_ES2015_BUILTIN_TYPEDARRAY=0
|
||||
JERRY_ES2015_CLASS=0
|
||||
JERRY_ES2015_FOR_OF=0
|
||||
JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER=0
|
||||
JERRY_ES2015_FUNCTION_REST_PARAMETER=0
|
||||
JERRY_ES2015_MODULE_SYSTEM=0
|
||||
@ -127,6 +128,8 @@ defined to `1`.
|
||||
Enables or disables the [ArrayBuffer](http://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-objects) and [TypedArray](http://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects) built-ins.
|
||||
* `JERRY_ES2015_CLASS`:
|
||||
Enables or disables the [class](https://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions) language element.
|
||||
* `JERRY_ES2015_FOR_OF`:
|
||||
Enables or disables the [for of](https://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements) language element.
|
||||
* `JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER`:
|
||||
Enables or disables the [default value](http://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions) for formal parameters.
|
||||
* `JERRY_ES2015_FUNCTION_REST_PARAMETER`:
|
||||
@ -150,6 +153,7 @@ defined to `1`.
|
||||
* `JERRY_ES2015_BUILTIN_SYMBOL`
|
||||
* `JERRY_ES2015_BUILTIN_TYPEDARRAY`
|
||||
* `JERRY_ES2015_CLASS`
|
||||
* `JERRY_ES2015_FOR_OF`
|
||||
* `JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER`
|
||||
* `JERRY_ES2015_FUNCTION_REST_PARAMETER`
|
||||
* `JERRY_ES2015_MODULE_SYSTEM`
|
||||
|
||||
@ -79,6 +79,16 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
||||
vm_stack_top_p -= PARSER_WITH_CONTEXT_STACK_ALLOCATION;
|
||||
break;
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
case VM_CONTEXT_FOR_OF:
|
||||
{
|
||||
ecma_free_value (vm_stack_top_p[-2]);
|
||||
ecma_free_value (vm_stack_top_p[-3]);
|
||||
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
vm_stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
default:
|
||||
{
|
||||
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1]) == VM_CONTEXT_FOR_IN);
|
||||
|
||||
@ -44,6 +44,9 @@ typedef enum
|
||||
VM_CONTEXT_SUPER_CLASS, /**< super class context */
|
||||
#endif /* ENABLED (JERRY_ES2015_CLASS) */
|
||||
VM_CONTEXT_FOR_IN, /**< for-in context */
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
VM_CONTEXT_FOR_OF, /**< for-of context */
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
} vm_stack_context_type_t;
|
||||
|
||||
ecma_value_t *vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *vm_stack_top_p);
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "ecma-function-object.h"
|
||||
#include "ecma-gc.h"
|
||||
#include "ecma-helpers.h"
|
||||
#include "ecma-iterator-object.h"
|
||||
#include "ecma-lcache.h"
|
||||
#include "ecma-lex-env.h"
|
||||
#include "ecma-objects.h"
|
||||
@ -2888,6 +2889,93 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
case VM_OC_FOR_OF_CREATE_CONTEXT:
|
||||
{
|
||||
ecma_value_t value = *(--stack_top_p);
|
||||
|
||||
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
||||
|
||||
ecma_value_t iterator = ecma_op_get_iterator (value, ECMA_VALUE_EMPTY);
|
||||
|
||||
ecma_free_value (value);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (iterator))
|
||||
{
|
||||
result = iterator;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ecma_value_t iterator_step = ecma_op_iterator_step (iterator);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (iterator_step))
|
||||
{
|
||||
ecma_free_value (iterator);
|
||||
result = iterator_step;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ecma_is_value_false (iterator_step))
|
||||
{
|
||||
ecma_free_value (iterator);
|
||||
byte_code_p = byte_code_start_p + branch_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p);
|
||||
|
||||
VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
stack_top_p += PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
|
||||
stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset);
|
||||
stack_top_p[-2] = iterator_step;
|
||||
stack_top_p[-3] = iterator;
|
||||
|
||||
continue;
|
||||
}
|
||||
case VM_OC_FOR_OF_GET_NEXT:
|
||||
{
|
||||
ecma_value_t *context_top_p = frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth;
|
||||
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (context_top_p[-1]) == VM_CONTEXT_FOR_OF);
|
||||
|
||||
ecma_value_t next_value = ecma_op_iterator_value (context_top_p[-2]);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (next_value))
|
||||
{
|
||||
result = next_value;
|
||||
goto error;
|
||||
}
|
||||
|
||||
*stack_top_p++ = next_value;
|
||||
continue;
|
||||
}
|
||||
case VM_OC_FOR_OF_HAS_NEXT:
|
||||
{
|
||||
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
||||
|
||||
ecma_value_t iterator_step = ecma_op_iterator_step (stack_top_p[-3]);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (iterator_step))
|
||||
{
|
||||
result = iterator_step;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!ecma_is_value_false (iterator_step))
|
||||
{
|
||||
ecma_free_value (stack_top_p[-2]);
|
||||
stack_top_p[-2] = iterator_step;
|
||||
byte_code_p = byte_code_start_p + branch_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
ecma_free_value (stack_top_p[-2]);
|
||||
ecma_free_value (stack_top_p[-3]);
|
||||
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
case VM_OC_TRY:
|
||||
{
|
||||
/* Try opcode simply creates the try context. */
|
||||
|
||||
@ -207,6 +207,11 @@ typedef enum
|
||||
VM_OC_FOR_IN_CREATE_CONTEXT, /**< for in create context */
|
||||
VM_OC_FOR_IN_GET_NEXT, /**< get next */
|
||||
VM_OC_FOR_IN_HAS_NEXT, /**< has next */
|
||||
#if ENABLED (JERRY_ES2015_FOR_OF)
|
||||
VM_OC_FOR_OF_CREATE_CONTEXT, /**< for of create context */
|
||||
VM_OC_FOR_OF_GET_NEXT, /**< get next */
|
||||
VM_OC_FOR_OF_HAS_NEXT, /**< has next */
|
||||
#endif /* ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
VM_OC_TRY, /**< try */
|
||||
VM_OC_CATCH, /**< catch */
|
||||
VM_OC_FINALLY, /**< finally */
|
||||
@ -269,6 +274,11 @@ typedef enum
|
||||
VM_OC_PUSH_CONSTRUCTOR_THIS = VM_OC_NONE, /**< push 'this' inside a class constructor */
|
||||
VM_OC_CONSTRUCTOR_RET = VM_OC_NONE, /**< explicit return from a class constructor */
|
||||
#endif /* !ENABLED (JERRY_ES2015_CLASS) */
|
||||
#if !ENABLED (JERRY_ES2015_FOR_OF)
|
||||
VM_OC_FOR_OF_CREATE_CONTEXT = VM_OC_NONE, /**< for of create context */
|
||||
VM_OC_FOR_OF_GET_NEXT = VM_OC_NONE, /**< get next */
|
||||
VM_OC_FOR_OF_HAS_NEXT = VM_OC_NONE, /**< has next */
|
||||
#endif /* !ENABLED (JERRY_ES2015_FOR_OF) */
|
||||
VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */
|
||||
} vm_oc_unused_types;
|
||||
|
||||
|
||||
117
tests/jerry/es2015/for-of.js
Normal file
117
tests/jerry/es2015/for-of.js
Normal file
@ -0,0 +1,117 @@
|
||||
// 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.
|
||||
|
||||
function parse (txt) {
|
||||
try {
|
||||
eval (txt)
|
||||
assert (false)
|
||||
} catch (e) {
|
||||
assert (e instanceof SyntaxError)
|
||||
}
|
||||
}
|
||||
|
||||
function checkError (obj) {
|
||||
try {
|
||||
for (var a of obj);
|
||||
assert (false)
|
||||
} catch (e) {
|
||||
assert (e instanceof TypeError)
|
||||
}
|
||||
}
|
||||
|
||||
var arr = [1,2,3,4]
|
||||
|
||||
var forOf =
|
||||
"for var prop of obj" +
|
||||
" obj [prop] += 4"
|
||||
parse (forOf)
|
||||
|
||||
var forOf =
|
||||
"for [var prop of obj]" +
|
||||
" obj[prop] += 4;"
|
||||
parse (forOf)
|
||||
|
||||
var forOf =
|
||||
"for (var prop obj)" +
|
||||
" obj[prop] += 4;"
|
||||
parse (forOf)
|
||||
|
||||
var forOf =
|
||||
"foreach (var prop of obj)" +
|
||||
" obj[prop] += 4;"
|
||||
parse (forOf)
|
||||
|
||||
checkError(5)
|
||||
|
||||
var obj = {}
|
||||
Object.defineProperty(obj, Symbol.iterator, { get : function () { throw TypeError ('foo');}});
|
||||
checkError (obj);
|
||||
|
||||
var obj = {
|
||||
[Symbol.iterator] : 5
|
||||
}
|
||||
|
||||
checkError (obj);
|
||||
|
||||
var obj = {
|
||||
[Symbol.iterator] () {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
checkError(obj);
|
||||
|
||||
var obj = {
|
||||
[Symbol.iterator] () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
checkError(obj);
|
||||
|
||||
var obj = {
|
||||
[Symbol.iterator] () {
|
||||
return {
|
||||
next() {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkError(obj);
|
||||
|
||||
var array = [0, 1, 2, 3, 4, 5];
|
||||
|
||||
var i = 0;
|
||||
for (var a of array) {
|
||||
assert (a === i++);
|
||||
}
|
||||
|
||||
var obj = {
|
||||
[Symbol.iterator]() {
|
||||
return {
|
||||
counter : 0,
|
||||
next () {
|
||||
if (this.counter == 10) {
|
||||
return { done : true, value : undefined };
|
||||
}
|
||||
return { done: false, value: this.counter++ };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
for (var a of obj) {
|
||||
assert (a === i++);
|
||||
}
|
||||
@ -223,7 +223,7 @@ main (void)
|
||||
/* Check the snapshot data. Unused bytes should be filled with zeroes */
|
||||
const uint8_t expected_data[] =
|
||||
{
|
||||
0x4A, 0x52, 0x52, 0x59, 0x16, 0x00, 0x00, 0x00,
|
||||
0x4A, 0x52, 0x52, 0x59, 0x17, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user