mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
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:
parent
be8ae3aae8
commit
0fd1ed6f27
@ -107,6 +107,7 @@ typedef enum
|
||||
ECMA_PARSE_HAS_STATIC_SUPER = (1u << 5), /**< the current context is a static class method */
|
||||
ECMA_PARSE_EVAL = (1u << 6), /**< eval is called */
|
||||
ECMA_PARSE_MODULE = (1u << 7), /**< module is parsed */
|
||||
ECMA_PARSE_FUNCTION = (1u << 8), /**< a function body is parsed or the code is inside a function */
|
||||
} ecma_parse_opts_t;
|
||||
|
||||
/**
|
||||
|
||||
@ -54,6 +54,10 @@ ecma_init (void)
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
|
||||
ecma_job_queue_init ();
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
JERRY_CONTEXT (current_new_target) = JERRY_CONTEXT_INVALID_NEW_TARGET;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
} /* ecma_init */
|
||||
|
||||
/**
|
||||
@ -62,6 +66,10 @@ ecma_init (void)
|
||||
void
|
||||
ecma_finalize (void)
|
||||
{
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
JERRY_ASSERT (JERRY_CONTEXT (current_new_target) == JERRY_CONTEXT_INVALID_NEW_TARGET);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
ecma_finalize_global_lex_env ();
|
||||
ecma_finalize_builtins ();
|
||||
ecma_gc_run ();
|
||||
|
||||
@ -99,6 +99,12 @@ ecma_op_eval_chars_buffer (const lit_utf8_byte_t *code_p, /**< code characters b
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
ECMA_CLEAR_SUPER_EVAL_PARSER_OPTS ();
|
||||
|
||||
/* If an eval is used inside the function the info should be propagated. */
|
||||
if (JERRY_CONTEXT (current_new_target) != JERRY_CONTEXT_INVALID_NEW_TARGET)
|
||||
{
|
||||
parse_opts |= ECMA_PARSE_FUNCTION;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
ecma_value_t parse_status = parser_parse_script (NULL,
|
||||
|
||||
@ -724,6 +724,162 @@ ecma_op_set_class_prototype (ecma_value_t completion_value, /**< completion_valu
|
||||
} /* ecma_op_set_class_prototype */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/**
|
||||
* Perform a JavaScript function object method call.
|
||||
*
|
||||
* The input function object should be a pure JavaScript method or
|
||||
* a builtin function implemented in C.
|
||||
*
|
||||
* In case of JERRY_ES2015 the arguments_list_p contains the information
|
||||
* wheter if the function was invoked with "new" or not.
|
||||
*
|
||||
* @return the result of the function call.
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_op_function_call_simple (ecma_object_t *func_obj_p, /**< Function object */
|
||||
ecma_value_t this_arg_value, /**< 'this' argument's value */
|
||||
const ecma_value_t *arguments_list_p, /**< arguments list */
|
||||
ecma_length_t arguments_list_len) /**< length of arguments list */
|
||||
{
|
||||
JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION);
|
||||
|
||||
if (JERRY_UNLIKELY (ecma_get_object_is_builtin (func_obj_p)))
|
||||
{
|
||||
JERRY_ASSERT (!ecma_op_function_has_construct_flag (arguments_list_p));
|
||||
ecma_value_t ret_value = ecma_builtin_dispatch_call (func_obj_p,
|
||||
this_arg_value,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
/* Entering Function Code (ECMA-262 v5, 10.4.3) */
|
||||
ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_obj_p;
|
||||
|
||||
ecma_object_t *scope_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
|
||||
ext_func_p->u.function.scope_cp);
|
||||
|
||||
/* 8. */
|
||||
ecma_value_t this_binding = this_arg_value;
|
||||
bool free_this_binding = false;
|
||||
|
||||
const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p);
|
||||
uint16_t status_flags = bytecode_data_p->status_flags;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (JERRY_UNLIKELY (status_flags & (CBC_CODE_FLAGS_CONSTRUCTOR | CBC_CODE_FLAGS_GENERATOR)))
|
||||
{
|
||||
bool is_construct_call = ecma_op_function_has_construct_flag (arguments_list_p);
|
||||
|
||||
if (!is_construct_call && (status_flags & CBC_CODE_FLAGS_CONSTRUCTOR))
|
||||
{
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'."));
|
||||
}
|
||||
if (is_construct_call && (status_flags & CBC_CODE_FLAGS_GENERATOR))
|
||||
{
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("Generator functions cannot be invoked with 'new'."));
|
||||
}
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/* 1. */
|
||||
if (!(status_flags & CBC_CODE_FLAGS_STRICT_MODE))
|
||||
{
|
||||
if (ecma_is_value_undefined (this_binding)
|
||||
|| ecma_is_value_null (this_binding))
|
||||
{
|
||||
/* 2. */
|
||||
this_binding = ecma_make_object_value (ecma_builtin_get_global ());
|
||||
}
|
||||
else if (!ecma_is_value_object (this_binding))
|
||||
{
|
||||
/* 3., 4. */
|
||||
this_binding = ecma_op_to_object (this_binding);
|
||||
free_this_binding = true;
|
||||
|
||||
JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (this_binding));
|
||||
}
|
||||
}
|
||||
|
||||
arguments_list_p = ecma_op_function_clear_construct_flag (arguments_list_p);
|
||||
|
||||
/* 5. */
|
||||
ecma_object_t *local_env_p;
|
||||
if (status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED)
|
||||
{
|
||||
local_env_p = scope_p;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_env_p = ecma_create_decl_lex_env (scope_p);
|
||||
if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_IS_ARGUMENTS_NEEDED)
|
||||
{
|
||||
ecma_op_create_arguments_object (func_obj_p,
|
||||
local_env_p,
|
||||
arguments_list_p,
|
||||
arguments_list_len,
|
||||
bytecode_data_p);
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (JERRY_UNLIKELY (status_flags & CBC_CODE_FLAGS_CONSTRUCTOR))
|
||||
{
|
||||
ecma_op_set_class_this_binding (local_env_p, this_binding);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
}
|
||||
|
||||
ecma_value_t ret_value = vm_run (bytecode_data_p,
|
||||
this_binding,
|
||||
local_env_p,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
|
||||
if (!(status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED))
|
||||
{
|
||||
ecma_deref_object (local_env_p);
|
||||
}
|
||||
|
||||
if (JERRY_UNLIKELY (free_this_binding))
|
||||
{
|
||||
ecma_free_value (this_binding);
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
} /* ecma_op_function_call_simple */
|
||||
|
||||
/**
|
||||
* Perform a native C method call which was registered via the API.
|
||||
*
|
||||
* @return the result of the function call.
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_op_function_call_external (ecma_object_t *func_obj_p, /**< Function object */
|
||||
ecma_value_t this_arg_value, /**< 'this' argument's value */
|
||||
const ecma_value_t *arguments_list_p, /**< arguments list */
|
||||
ecma_length_t arguments_list_len) /**< length of arguments list */
|
||||
|
||||
{
|
||||
JERRY_ASSERT (!ecma_op_function_has_construct_flag (arguments_list_p));
|
||||
ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p;
|
||||
JERRY_ASSERT (ext_func_obj_p->u.external_handler_cb != NULL);
|
||||
|
||||
ecma_value_t ret_value = ext_func_obj_p->u.external_handler_cb (ecma_make_object_value (func_obj_p),
|
||||
this_arg_value,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
if (JERRY_UNLIKELY (ecma_is_value_error_reference (ret_value)))
|
||||
{
|
||||
ecma_raise_error_from_error_reference (ret_value);
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_DEBUGGER)
|
||||
JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN);
|
||||
#endif /* ENABLED (JERRY_DEBUGGER) */
|
||||
return ret_value;
|
||||
} /* ecma_op_function_call_external */
|
||||
|
||||
/**
|
||||
* [[Call]] implementation for Function objects,
|
||||
* created through 13.2 (ECMA_OBJECT_TYPE_FUNCTION)
|
||||
@ -755,128 +911,40 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
|
||||
{
|
||||
case ECMA_OBJECT_TYPE_FUNCTION:
|
||||
{
|
||||
if (JERRY_UNLIKELY (ecma_get_object_is_builtin (func_obj_p)))
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target);
|
||||
if (JERRY_LIKELY (!ecma_get_object_is_builtin (func_obj_p)))
|
||||
{
|
||||
JERRY_ASSERT (!ecma_op_function_has_construct_flag (arguments_list_p));
|
||||
JERRY_CONTEXT (current_new_target) = NULL;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
ecma_value_t ret_value = ecma_builtin_dispatch_call (func_obj_p,
|
||||
jerry_value_t result = ecma_op_function_call_simple (func_obj_p,
|
||||
this_arg_value,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
JERRY_CONTEXT (current_new_target) = old_new_target;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
return result;
|
||||
}
|
||||
case ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION:
|
||||
{
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target);
|
||||
JERRY_CONTEXT (current_new_target) = NULL;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
jerry_value_t result = ecma_op_function_call_external (func_obj_p,
|
||||
this_arg_value,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
/* Entering Function Code (ECMA-262 v5, 10.4.3) */
|
||||
ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_obj_p;
|
||||
|
||||
ecma_object_t *scope_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
|
||||
ext_func_p->u.function.scope_cp);
|
||||
|
||||
/* 8. */
|
||||
ecma_value_t this_binding = this_arg_value;
|
||||
bool free_this_binding = false;
|
||||
|
||||
const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p);
|
||||
uint16_t status_flags = bytecode_data_p->status_flags;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (JERRY_UNLIKELY (status_flags & (CBC_CODE_FLAGS_CONSTRUCTOR | CBC_CODE_FLAGS_GENERATOR)))
|
||||
{
|
||||
if ((status_flags & CBC_CODE_FLAGS_CONSTRUCTOR) && !ecma_op_function_has_construct_flag (arguments_list_p))
|
||||
{
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'."));
|
||||
}
|
||||
if ((status_flags & CBC_CODE_FLAGS_GENERATOR) && ecma_op_function_has_construct_flag (arguments_list_p))
|
||||
{
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("Generator functions cannot be invoked with 'new'."));
|
||||
}
|
||||
}
|
||||
JERRY_CONTEXT (current_new_target) = old_new_target;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/* 1. */
|
||||
if (!(status_flags & CBC_CODE_FLAGS_STRICT_MODE))
|
||||
{
|
||||
if (ecma_is_value_undefined (this_binding)
|
||||
|| ecma_is_value_null (this_binding))
|
||||
{
|
||||
/* 2. */
|
||||
this_binding = ecma_make_object_value (ecma_builtin_get_global ());
|
||||
}
|
||||
else if (!ecma_is_value_object (this_binding))
|
||||
{
|
||||
/* 3., 4. */
|
||||
this_binding = ecma_op_to_object (this_binding);
|
||||
free_this_binding = true;
|
||||
|
||||
JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (this_binding));
|
||||
}
|
||||
}
|
||||
|
||||
arguments_list_p = ecma_op_function_clear_construct_flag (arguments_list_p);
|
||||
|
||||
/* 5. */
|
||||
ecma_object_t *local_env_p;
|
||||
if (status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED)
|
||||
{
|
||||
local_env_p = scope_p;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_env_p = ecma_create_decl_lex_env (scope_p);
|
||||
if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_IS_ARGUMENTS_NEEDED)
|
||||
{
|
||||
ecma_op_create_arguments_object (func_obj_p,
|
||||
local_env_p,
|
||||
arguments_list_p,
|
||||
arguments_list_len,
|
||||
bytecode_data_p);
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (JERRY_UNLIKELY (status_flags & CBC_CODE_FLAGS_CONSTRUCTOR))
|
||||
{
|
||||
ecma_op_set_class_this_binding (local_env_p, this_binding);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
}
|
||||
|
||||
ecma_value_t ret_value = vm_run (bytecode_data_p,
|
||||
this_binding,
|
||||
local_env_p,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
|
||||
if (!(status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED))
|
||||
{
|
||||
ecma_deref_object (local_env_p);
|
||||
}
|
||||
|
||||
if (JERRY_UNLIKELY (free_this_binding))
|
||||
{
|
||||
ecma_free_value (this_binding);
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
case ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION:
|
||||
{
|
||||
ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p;
|
||||
|
||||
ecma_value_t ret_value = ext_func_obj_p->u.external_handler_cb (ecma_make_object_value (func_obj_p),
|
||||
this_arg_value,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
|
||||
if (JERRY_UNLIKELY (ecma_is_value_error_reference (ret_value)))
|
||||
{
|
||||
ecma_raise_error_from_error_reference (ret_value);
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_DEBUGGER)
|
||||
JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN);
|
||||
#endif /* ENABLED (JERRY_DEBUGGER) */
|
||||
return ret_value;
|
||||
return result;
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
case ECMA_OBJECT_TYPE_ARROW_FUNCTION:
|
||||
@ -1156,6 +1224,15 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */
|
||||
this_arg_value = ecma_make_object_value (new_this_obj_p);
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target);
|
||||
if (JERRY_LIKELY (new_this_obj_p != NULL))
|
||||
{
|
||||
/* This is not a super call so update the "new.target" */
|
||||
JERRY_CONTEXT (current_new_target) = func_obj_p;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/* 8. */
|
||||
ecma_value_t ret_value;
|
||||
|
||||
@ -1164,11 +1241,7 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */
|
||||
case ECMA_OBJECT_TYPE_FUNCTION:
|
||||
{
|
||||
arguments_list_p = ecma_op_function_set_construct_flag (arguments_list_p);
|
||||
|
||||
ret_value = ecma_op_function_call (func_obj_p,
|
||||
this_arg_value,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
ret_value = ecma_op_function_call_simple (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len);
|
||||
break;
|
||||
}
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
@ -1209,15 +1282,16 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
ret_value = ecma_op_function_call_external (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len);
|
||||
|
||||
ret_value = ecma_op_function_call (func_obj_p,
|
||||
this_arg_value,
|
||||
arguments_list_p,
|
||||
arguments_list_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
JERRY_CONTEXT (current_new_target) = old_new_target;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/* 9. */
|
||||
if (ECMA_IS_VALUE_ERROR (ret_value) || ecma_is_value_object (ret_value))
|
||||
{
|
||||
|
||||
@ -220,8 +220,22 @@ struct jerry_context_t
|
||||
/** hash table for caching the last access of properties */
|
||||
ecma_lcache_hash_entry_t lcache[ECMA_LCACHE_HASH_ROWS_COUNT][ECMA_LCACHE_HASH_ROW_LENGTH];
|
||||
#endif /* ENABLED (JERRY_LCACHE) */
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/**
|
||||
* Allowed values and it's meaning:
|
||||
* * NULL (0x0): the current "new.target" is undefined, that is the execution is inside a normal method.
|
||||
* * JERRY_CONTEXT_INVALID_NEW_TARGET (0x1): the current "new.target" is invalid, that is outside of a method.
|
||||
* * Any other valid function object pointer: the current "new.target" is valid and it is constructor call.
|
||||
*/
|
||||
ecma_object_t *current_new_target;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
};
|
||||
|
||||
/**
|
||||
* Magic constant used to indicate that the current "new.target" is not inside a function.
|
||||
*/
|
||||
#define JERRY_CONTEXT_INVALID_NEW_TARGET ((ecma_object_t *) 0x1)
|
||||
|
||||
#if ENABLED (JERRY_EXTERNAL_CONTEXT)
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -665,8 +665,13 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /*
|
||||
executable_object_p->frame_ctx.prev_context_p = JERRY_CONTEXT (vm_top_context_p);
|
||||
JERRY_CONTEXT (vm_top_context_p) = &executable_object_p->frame_ctx;
|
||||
|
||||
/* inside the generators the "new.target" is always "undefined" as it can't be invoked with "new" */
|
||||
ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target);
|
||||
JERRY_CONTEXT (current_new_target) = NULL;
|
||||
|
||||
ecma_value_t result = vm_execute (&executable_object_p->frame_ctx);
|
||||
|
||||
JERRY_CONTEXT (current_new_target) = old_new_target;
|
||||
executable_object_p->extended_object.u.class_prop.extra_info &= (uint16_t) ~ECMA_EXECUTABLE_OBJECT_RUNNING;
|
||||
|
||||
if (executable_object_p->frame_ctx.call_operation != VM_EXEC_RETURN)
|
||||
|
||||
@ -2137,6 +2137,21 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
*stack_top_p++ = ecma_copy_value (collection_p->buffer_p[tagged_idx]);
|
||||
continue;
|
||||
}
|
||||
case VM_OC_PUSH_NEW_TARGET:
|
||||
{
|
||||
ecma_object_t *new_target_object = JERRY_CONTEXT (current_new_target);
|
||||
if (new_target_object == NULL)
|
||||
{
|
||||
*stack_top_p++ = ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_ASSERT (new_target_object != JERRY_CONTEXT_INVALID_NEW_TARGET);
|
||||
ecma_ref_object (new_target_object);
|
||||
*stack_top_p++ = ecma_make_object_value (new_target_object);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
case VM_OC_PUSH_ELISON:
|
||||
{
|
||||
|
||||
@ -261,6 +261,7 @@ typedef enum
|
||||
VM_OC_RETURN_PROMISE, /**< return from an async function */
|
||||
VM_OC_STRING_CONCAT, /**< string concatenation */
|
||||
VM_OC_GET_TEMPLATE_OBJECT, /**< GetTemplateObject operation */
|
||||
VM_OC_PUSH_NEW_TARGET, /**< push new.target onto the stack */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
VM_OC_NONE, /**< a special opcode for unsupported byte codes */
|
||||
} vm_oc_types;
|
||||
@ -317,6 +318,7 @@ typedef enum
|
||||
VM_OC_RETURN_PROMISE = VM_OC_NONE, /**< return from an async function */
|
||||
VM_OC_STRING_CONCAT = VM_OC_NONE, /**< string concatenation */
|
||||
VM_OC_GET_TEMPLATE_OBJECT = VM_OC_NONE, /**< GetTemplateObject operation */
|
||||
VM_OC_PUSH_NEW_TARGET = VM_OC_NONE, /**< push new.target onto the stack */
|
||||
#endif /* !ENABLED (JERRY_ES2015) */
|
||||
|
||||
VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */
|
||||
|
||||
20
tests/jerry/es2015/new-target-async.js
Normal file
20
tests/jerry/es2015/new-target-async.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
async function async_method () {
|
||||
assert (new.target === undefined);
|
||||
}
|
||||
|
||||
async_method ();
|
||||
63
tests/jerry/es2015/new-target-class.js
Normal file
63
tests/jerry/es2015/new-target-class.js
Normal file
@ -0,0 +1,63 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
class Simple {
|
||||
constructor () {
|
||||
assert (new.target === Simple);
|
||||
this.called = 0;
|
||||
}
|
||||
|
||||
get getter () {
|
||||
this.called++;
|
||||
return new.target;
|
||||
}
|
||||
|
||||
set setter (val) {
|
||||
assert (new.target === undefined);
|
||||
this.called++;
|
||||
}
|
||||
}
|
||||
|
||||
var smp = new Simple ();
|
||||
assert (smp.getter === undefined);
|
||||
assert (smp.called === 1);
|
||||
smp.setter = -1;
|
||||
assert (smp.called === 2);
|
||||
|
||||
class BaseA {
|
||||
constructor () {
|
||||
assert (new.target === SubA);
|
||||
}
|
||||
}
|
||||
|
||||
class SubA extends BaseA {
|
||||
constructor () {
|
||||
super ();
|
||||
assert (new.target === SubA);
|
||||
}
|
||||
}
|
||||
|
||||
new SubA ();
|
||||
|
||||
/* TODO: enable if there is class name support. */
|
||||
/*
|
||||
class Named {
|
||||
constructor () {
|
||||
assert(new.target.name == "Named");
|
||||
}
|
||||
}
|
||||
|
||||
new Named ();
|
||||
*/
|
||||
33
tests/jerry/es2015/new-target-generator.js
Normal file
33
tests/jerry/es2015/new-target-generator.js
Normal file
@ -0,0 +1,33 @@
|
||||
/* 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 *demo_gen () {
|
||||
for (var idx = 0; idx < 3; idx++)
|
||||
{
|
||||
assert (new.target === undefined);
|
||||
yield idx;
|
||||
assert (new.target === undefined);
|
||||
}
|
||||
}
|
||||
|
||||
var gen = demo_gen ();
|
||||
|
||||
var value = 0;
|
||||
for (var item of gen)
|
||||
{
|
||||
value = item;
|
||||
}
|
||||
|
||||
assert (value === 2);
|
||||
137
tests/jerry/es2015/new-target.js
Normal file
137
tests/jerry/es2015/new-target.js
Normal file
@ -0,0 +1,137 @@
|
||||
/* 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 null_target () {
|
||||
assert (new.target === undefined);
|
||||
}
|
||||
|
||||
function demo () {
|
||||
null_target ();
|
||||
return new.target;
|
||||
}
|
||||
|
||||
assert (demo () === undefined);
|
||||
assert ((new demo ()) === demo);
|
||||
|
||||
/* new.target is only valid inside functions */
|
||||
try {
|
||||
eval ("new.target");
|
||||
assert (false);
|
||||
} catch (ex) {
|
||||
assert (ex instanceof SyntaxError);
|
||||
}
|
||||
|
||||
try {
|
||||
var eval_other = eval;
|
||||
eval_other ("new.target");
|
||||
assert (false);
|
||||
} catch (ex) {
|
||||
assert (ex instanceof SyntaxError);
|
||||
}
|
||||
|
||||
/* test with arrow function */
|
||||
var arrow_called = false;
|
||||
function arrow () {
|
||||
assert (new.target === arrow);
|
||||
var mth = () => { return new.target; }
|
||||
assert (mth () === arrow);
|
||||
arrow_called = true;
|
||||
}
|
||||
|
||||
new arrow ();
|
||||
assert (arrow_called === true);
|
||||
|
||||
/* test unary operation */
|
||||
var f_called = false;
|
||||
function f () {
|
||||
assert (isNaN (-new.target));
|
||||
f_called = true;
|
||||
}
|
||||
|
||||
new f ();
|
||||
assert (f_called === true);
|
||||
|
||||
/* test property access */
|
||||
function fg (callback_object) {
|
||||
callback_object.value = new.target.value;
|
||||
}
|
||||
|
||||
fg.value = 22;
|
||||
|
||||
var test_obj = {};
|
||||
new fg (test_obj);
|
||||
|
||||
assert (test_obj.value === 22);
|
||||
|
||||
|
||||
/* test new.target with eval */
|
||||
function eval_test () {
|
||||
var target = eval ("new.target");
|
||||
assert (target === eval_test);
|
||||
}
|
||||
|
||||
new eval_test ();
|
||||
|
||||
function eval_eval_test () {
|
||||
var target = eval ('eval("new.target")');
|
||||
assert (target === eval_eval_test);
|
||||
}
|
||||
|
||||
new eval_eval_test ();
|
||||
|
||||
|
||||
/* test assignment of the "new.target" */
|
||||
function expect_syntax_error (src)
|
||||
{
|
||||
try {
|
||||
eval (src);
|
||||
assert (false);
|
||||
} catch (ex) {
|
||||
assert (ex instanceof SyntaxError);
|
||||
}
|
||||
}
|
||||
|
||||
expect_syntax_error ("function assign () { new.target = 3; }");
|
||||
expect_syntax_error ("function assign () { new.target += 3; }");
|
||||
expect_syntax_error ("function assign () { new.target *= 3; }");
|
||||
expect_syntax_error ("function assign () { new.target -= 3; }");
|
||||
expect_syntax_error ("function assign () { new.target |= 3; }");
|
||||
expect_syntax_error ("function assign () { new.target &= 3; }");
|
||||
|
||||
expect_syntax_error ("function assign () { new.target++; }");
|
||||
expect_syntax_error ("function assign () { ++new.target; }");
|
||||
expect_syntax_error ("function assign () { new.target--; }");
|
||||
expect_syntax_error ("function assign () { --new.target; }");
|
||||
|
||||
expect_syntax_error ("function synt () { new....target; }");
|
||||
|
||||
function delete_test () {
|
||||
assert ((delete new.target) === true);
|
||||
}
|
||||
|
||||
new delete_test ();
|
||||
|
||||
function binary_test_1 () {
|
||||
/*/ new.target is converted to string */
|
||||
assert ((new.target + 1) === "function(){/* ecmascript */}1");
|
||||
}
|
||||
function binary_test_2 () { assert (isNaN (new.target - 3)); }
|
||||
function binary_test_3 () { assert (isNaN (new.target * 2)); }
|
||||
function binary_test_4 () { assert (isNaN (new.target / 4)); }
|
||||
|
||||
new binary_test_1 ();
|
||||
new binary_test_2 ();
|
||||
new binary_test_3 ();
|
||||
new binary_test_4 ();
|
||||
223
tests/unit-core/test-newtarget.c
Normal file
223
tests/unit-core/test-newtarget.c
Normal file
@ -0,0 +1,223 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#include "jerryscript.h"
|
||||
#include "jerryscript-port.h"
|
||||
#include "jerryscript-port-default.h"
|
||||
#include "test-common.h"
|
||||
|
||||
/**
|
||||
* Register a JavaScript function in the global object.
|
||||
*/
|
||||
static jerry_value_t
|
||||
register_js_function (const char *name_p, /**< name of the function */
|
||||
jerry_external_handler_t handler_p) /**< function callback */
|
||||
{
|
||||
jerry_value_t global_obj_val = jerry_get_global_object ();
|
||||
|
||||
jerry_value_t function_val = jerry_create_external_function (handler_p);
|
||||
jerry_value_t function_name_val = jerry_create_string ((const jerry_char_t *) name_p);
|
||||
jerry_value_t result_val = jerry_set_property (global_obj_val, function_name_val, function_val);
|
||||
|
||||
jerry_release_value (function_name_val);
|
||||
jerry_release_value (global_obj_val);
|
||||
|
||||
jerry_release_value (result_val);
|
||||
|
||||
return function_val;
|
||||
} /* register_js_function */
|
||||
|
||||
enum
|
||||
{
|
||||
TEST_ID_SIMPLE_CONSTRUCT = 1,
|
||||
TEST_ID_SIMPLE_CALL = 2,
|
||||
TEST_ID_CONSTRUCT_AND_CALL_SUB = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper method to access the "new.target" via an eval call.
|
||||
*/
|
||||
static jerry_value_t
|
||||
get_new_target (void)
|
||||
{
|
||||
const char *src = "new.target";
|
||||
return jerry_eval ((const jerry_char_t *) src, strlen (src), 0);
|
||||
} /* get_new_target */
|
||||
|
||||
static jerry_value_t
|
||||
construct_handler (const jerry_value_t func_obj_val, /**< function object */
|
||||
const jerry_value_t this_val, /**< this arg */
|
||||
const jerry_value_t args_p[], /**< function arguments */
|
||||
const jerry_length_t args_cnt) /**< number of function arguments */
|
||||
{
|
||||
JERRY_UNUSED (func_obj_val);
|
||||
JERRY_UNUSED (this_val);
|
||||
JERRY_UNUSED (args_p);
|
||||
|
||||
if (args_cnt != 1 || !jerry_value_is_number (args_p[0]))
|
||||
{
|
||||
TEST_ASSERT (0 && "Invalid arguments for demo method");
|
||||
}
|
||||
|
||||
int test_id = (int) jerry_get_number_value (args_p[0]);
|
||||
|
||||
switch (test_id)
|
||||
{
|
||||
case TEST_ID_SIMPLE_CONSTRUCT:
|
||||
{
|
||||
/* Method was called with "new": new.target should be equal to the function object. */
|
||||
jerry_value_t target = get_new_target ();
|
||||
TEST_ASSERT (!jerry_value_is_undefined (target));
|
||||
TEST_ASSERT (target == func_obj_val);
|
||||
jerry_release_value (target);
|
||||
break;
|
||||
}
|
||||
case TEST_ID_SIMPLE_CALL:
|
||||
{
|
||||
/* Method was called directly without "new": new.target should be equal undefined. */
|
||||
jerry_value_t target = get_new_target ();
|
||||
TEST_ASSERT (jerry_value_is_undefined (target));
|
||||
TEST_ASSERT (target != func_obj_val);
|
||||
jerry_release_value (target);
|
||||
break;
|
||||
}
|
||||
case TEST_ID_CONSTRUCT_AND_CALL_SUB:
|
||||
{
|
||||
/* Method was called with "new": new.target should be equal to the function object. */
|
||||
jerry_value_t target = get_new_target ();
|
||||
TEST_ASSERT (!jerry_value_is_undefined (target));
|
||||
TEST_ASSERT (target == func_obj_val);
|
||||
jerry_release_value (target);
|
||||
|
||||
/* Calling a function should hide the old "new.target". */
|
||||
jerry_value_t sub_arg = jerry_create_number (TEST_ID_SIMPLE_CALL);
|
||||
jerry_value_t func_call_result = jerry_call_function (func_obj_val, this_val, &sub_arg, 1);
|
||||
TEST_ASSERT (!jerry_value_is_error (func_call_result));
|
||||
TEST_ASSERT (jerry_value_is_undefined (func_call_result));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
TEST_ASSERT (0 && "Incorrect test ID");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return jerry_create_undefined ();
|
||||
} /* construct_handler */
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
/* Test JERRY_FEATURE_SYMBOL feature as it is a must-have in ES2015 */
|
||||
if (!jerry_is_feature_enabled (JERRY_FEATURE_SYMBOL))
|
||||
{
|
||||
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Skipping test, ES2015 support is disabled.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
jerry_init (JERRY_INIT_EMPTY);
|
||||
|
||||
jerry_value_t demo_func = register_js_function ("Demo", construct_handler);
|
||||
|
||||
{
|
||||
jerry_value_t test_arg = jerry_create_number (TEST_ID_SIMPLE_CONSTRUCT);
|
||||
jerry_value_t constructed = jerry_construct_object (demo_func, &test_arg, 1);
|
||||
TEST_ASSERT (!jerry_value_is_error (constructed));
|
||||
TEST_ASSERT (jerry_value_is_object (constructed));
|
||||
jerry_release_value (test_arg);
|
||||
jerry_release_value (constructed);
|
||||
}
|
||||
|
||||
{
|
||||
jerry_value_t test_arg = jerry_create_number (TEST_ID_SIMPLE_CALL);
|
||||
jerry_value_t this_arg = jerry_create_undefined ();
|
||||
jerry_value_t constructed = jerry_call_function (demo_func, this_arg, &test_arg, 1);
|
||||
TEST_ASSERT (jerry_value_is_undefined (constructed));
|
||||
jerry_release_value (constructed);
|
||||
jerry_release_value (constructed);
|
||||
jerry_release_value (test_arg);
|
||||
}
|
||||
|
||||
{
|
||||
jerry_value_t test_arg = jerry_create_number (TEST_ID_CONSTRUCT_AND_CALL_SUB);
|
||||
jerry_value_t constructed = jerry_construct_object (demo_func, &test_arg, 1);
|
||||
TEST_ASSERT (!jerry_value_is_error (constructed));
|
||||
TEST_ASSERT (jerry_value_is_object (constructed));
|
||||
jerry_release_value (test_arg);
|
||||
jerry_release_value (constructed);
|
||||
}
|
||||
|
||||
{
|
||||
static const jerry_char_t test_source[] = TEST_STRING_LITERAL ("new Demo (1)");
|
||||
|
||||
jerry_value_t parsed_code_val = jerry_parse (NULL,
|
||||
0,
|
||||
test_source,
|
||||
sizeof (test_source) - 1,
|
||||
JERRY_PARSE_NO_OPTS);
|
||||
TEST_ASSERT (!jerry_value_is_error (parsed_code_val));
|
||||
|
||||
jerry_value_t res = jerry_run (parsed_code_val);
|
||||
TEST_ASSERT (!jerry_value_is_error (res));
|
||||
|
||||
jerry_release_value (res);
|
||||
jerry_release_value (parsed_code_val);
|
||||
}
|
||||
|
||||
{
|
||||
static const jerry_char_t test_source[] = TEST_STRING_LITERAL ("Demo (2)");
|
||||
|
||||
jerry_value_t parsed_code_val = jerry_parse (NULL,
|
||||
0,
|
||||
test_source,
|
||||
sizeof (test_source) - 1,
|
||||
JERRY_PARSE_NO_OPTS);
|
||||
TEST_ASSERT (!jerry_value_is_error (parsed_code_val));
|
||||
|
||||
jerry_value_t res = jerry_run (parsed_code_val);
|
||||
TEST_ASSERT (!jerry_value_is_error (res));
|
||||
|
||||
jerry_release_value (res);
|
||||
jerry_release_value (parsed_code_val);
|
||||
}
|
||||
|
||||
{
|
||||
static const jerry_char_t test_source[] = TEST_STRING_LITERAL (
|
||||
"function base(arg) { new Demo (arg); };"
|
||||
"base (1);"
|
||||
"new base(1);"
|
||||
"new base(3);"
|
||||
);
|
||||
|
||||
jerry_value_t parsed_code_val = jerry_parse (NULL,
|
||||
0,
|
||||
test_source,
|
||||
sizeof (test_source) - 1,
|
||||
JERRY_PARSE_NO_OPTS);
|
||||
TEST_ASSERT (!jerry_value_is_error (parsed_code_val));
|
||||
|
||||
jerry_value_t res = jerry_run (parsed_code_val);
|
||||
TEST_ASSERT (!jerry_value_is_error (res));
|
||||
|
||||
jerry_release_value (res);
|
||||
jerry_release_value (parsed_code_val);
|
||||
}
|
||||
|
||||
jerry_release_value (demo_func);
|
||||
jerry_cleanup ();
|
||||
return 0;
|
||||
} /* main */
|
||||
Loading…
x
Reference in New Issue
Block a user