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:
Péter Gál
2020-01-14 13:34:19 +01:00
committed by Robert Fancsik
parent be8ae3aae8
commit 0fd1ed6f27
22 changed files with 841 additions and 124 deletions
+2
View File
@@ -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, \
+49
View File
@@ -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);
+8 -1
View File
@@ -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);
}
+8
View File
@@ -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:
+4
View File
@@ -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,
+2
View File
@@ -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*/
+36
View File
@@ -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) */
/**
+7
View File
@@ -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: