Add support for new.target (#3469)
Notable changes:
* Extracted the pure JS/builtin and external C method invocations
into two new methods (`ecma_op_function_call_{simple, external}`).
* Updated parser/scanner to handle "new.target" correctly.
* Added JS test case.
JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.u-szeged@partner.samsung.com
This commit is contained in:
committed by
Robert Fancsik
parent
be8ae3aae8
commit
0fd1ed6f27
@@ -680,6 +680,8 @@
|
||||
VM_OC_EXT_RETURN | VM_OC_GET_STACK) \
|
||||
CBC_OPCODE (CBC_EXT_RETURN_PROMISE, CBC_NO_FLAG, -1, \
|
||||
VM_OC_RETURN_PROMISE | VM_OC_GET_STACK) \
|
||||
CBC_OPCODE (CBC_EXT_PUSH_NEW_TARGET, CBC_NO_FLAG, 1, \
|
||||
VM_OC_PUSH_NEW_TARGET | VM_OC_PUT_STACK) \
|
||||
\
|
||||
/* Last opcode (not a real opcode). */ \
|
||||
CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, \
|
||||
|
||||
@@ -113,6 +113,27 @@ parser_check_invalid_assign (parser_context_t *context_p) /**< context */
|
||||
}
|
||||
} /* parser_check_invalid_assign */
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/**
|
||||
* Check and throw an error if the "new.target" is invalid as a left-hand side expression.
|
||||
*/
|
||||
static void
|
||||
parser_check_invalid_new_target (parser_context_t *context_p, /**< parser context */
|
||||
cbc_opcode_t opcode) /**< current opcode under parsing */
|
||||
{
|
||||
JERRY_ASSERT ((opcode >= CBC_PRE_INCR && opcode <= CBC_POST_DECR)
|
||||
|| (opcode == CBC_ASSIGN
|
||||
&& (context_p->token.type == LEXER_ASSIGN
|
||||
|| LEXER_IS_BINARY_LVALUE_TOKEN (context_p->token.type))));
|
||||
|
||||
/* new.target is an invalid left-hand side target */
|
||||
if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_NEW_TARGET))
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_NOT_ALLOWED);
|
||||
}
|
||||
} /* parser_check_invalid_new_target */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/**
|
||||
* Emit identifier reference
|
||||
*/
|
||||
@@ -203,6 +224,10 @@ parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */
|
||||
return;
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
parser_check_invalid_new_target (context_p, opcode);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
||||
}
|
||||
|
||||
@@ -1380,6 +1405,22 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */
|
||||
{
|
||||
/* After 'new' unary operators are not allowed. */
|
||||
new_was_seen = true;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/* Check if "new.target" is written here. */
|
||||
if (scanner_try_scan_new_target (context_p))
|
||||
{
|
||||
if (!(context_p->status_flags & PARSER_IS_FUNCTION))
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_NEW_TARGET);
|
||||
lexer_next_token (context_p);
|
||||
/* Found "new.target" return here */
|
||||
return false;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
}
|
||||
else if (new_was_seen
|
||||
|| (*grouping_level_p == PARSE_EXPR_LEFT_HAND_SIDE)
|
||||
@@ -2166,6 +2207,10 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**<
|
||||
else
|
||||
{
|
||||
/* Invalid LeftHandSide expression. */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
parser_check_invalid_new_target (context_p, CBC_ASSIGN);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
||||
parser_stack_push_uint8 (context_p, CBC_ASSIGN);
|
||||
}
|
||||
@@ -2205,6 +2250,10 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */
|
||||
else
|
||||
{
|
||||
/* Invalid LeftHandSide expression. */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
parser_check_invalid_new_target (context_p, CBC_ASSIGN);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
||||
parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE);
|
||||
}
|
||||
|
||||
@@ -707,6 +707,7 @@ void scanner_cleanup (parser_context_t *context_p);
|
||||
bool scanner_is_context_needed (parser_context_t *context_p);
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
bool scanner_is_global_context_needed (parser_context_t *context_p);
|
||||
bool scanner_try_scan_new_target (parser_context_t *context_p);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
void scanner_create_variables (parser_context_t *context_p, uint32_t option_flags);
|
||||
|
||||
|
||||
@@ -3044,7 +3044,14 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
options |= PARSE_EXPR_HAS_LITERAL;
|
||||
}
|
||||
|
||||
if (context_p->status_flags & PARSER_IS_FUNCTION)
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
bool is_eval = (context_p->status_flags & PARSER_IS_EVAL) != 0;
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
/* In case of ES5.1 it does not matter that this is an eval parsing or not. */
|
||||
bool is_eval = false;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
if ((context_p->status_flags & PARSER_IS_FUNCTION) && !is_eval)
|
||||
{
|
||||
parser_parse_expression_statement (context_p, options);
|
||||
}
|
||||
|
||||
@@ -1181,6 +1181,14 @@ parser_error_to_string (parser_error_t error) /**< error code */
|
||||
{
|
||||
return "Rest parameter may not have a default initializer.";
|
||||
}
|
||||
case PARSER_ERR_NEW_TARGET_EXPECTED:
|
||||
{
|
||||
return "Expected new.target expression.";
|
||||
}
|
||||
case PARSER_ERR_NEW_TARGET_NOT_ALLOWED:
|
||||
{
|
||||
return "new.target expression is not allowed here.";
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
||||
case PARSER_ERR_FILE_NOT_FOUND:
|
||||
|
||||
@@ -1982,6 +1982,10 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
|
||||
{
|
||||
context.status_flags |= PARSER_IS_EVAL;
|
||||
}
|
||||
if (parse_opts & ECMA_PARSE_FUNCTION)
|
||||
{
|
||||
context.status_flags |= PARSER_IS_FUNCTION;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
scanner_scan_all (&context,
|
||||
|
||||
@@ -144,6 +144,8 @@ typedef enum
|
||||
PARSER_ERR_DUPLICATED_ARGUMENT_NAMES, /**< duplicated argument names */
|
||||
PARSER_ERR_INVALID_DESTRUCTURING_PATTERN, /**< invalid destructuring pattern */
|
||||
PARSER_ERR_ILLEGAL_PROPERTY_IN_DECLARATION, /**< illegal property in declaration context */
|
||||
PARSER_ERR_NEW_TARGET_EXPECTED, /**< expected new.target expression */
|
||||
PARSER_ERR_NEW_TARGET_NOT_ALLOWED, /**< new.target is not allowed in the given context */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
||||
PARSER_ERR_FILE_NOT_FOUND, /**< file not found*/
|
||||
|
||||
@@ -1752,6 +1752,42 @@ scanner_is_global_context_needed (parser_context_t *context_p) /**< context */
|
||||
return false;
|
||||
} /* scanner_is_global_context_needed */
|
||||
|
||||
/**
|
||||
* Try to scan/parse the ".target" part in the "new.target" expression.
|
||||
*
|
||||
* Upon exiting with "true" the current token will point to the "target"
|
||||
* literal.
|
||||
*
|
||||
* If the "target" literal is not after the "new." then a scanner/parser
|
||||
* error will be raised.
|
||||
*
|
||||
* @returns true if the ".target" part was found
|
||||
* false if there is no "." after the new.
|
||||
*/
|
||||
bool
|
||||
scanner_try_scan_new_target (parser_context_t *context_p) /**< parser/scanner context */
|
||||
{
|
||||
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_NEW);
|
||||
|
||||
if (lexer_check_next_character (context_p, LIT_CHAR_DOT))
|
||||
{
|
||||
lexer_next_token (context_p);
|
||||
if (context_p->token.type != LEXER_DOT)
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER);
|
||||
}
|
||||
|
||||
lexer_next_token (context_p);
|
||||
if (!lexer_token_is_identifier (context_p, "target", 6))
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_EXPECTED);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} /* scanner_try_scan_new_target */
|
||||
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,6 +75,13 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */
|
||||
case LEXER_KEYW_NEW:
|
||||
{
|
||||
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (scanner_try_scan_new_target (context_p))
|
||||
{
|
||||
scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
break;
|
||||
}
|
||||
case LEXER_DIVIDE:
|
||||
|
||||
Reference in New Issue
Block a user