Improve Proxy recrusion check in their internal methods. (#4568)
In some parts of Proxy internal methods the compiler can do a bit of tail call optimalization which would be ok, but the current stack limit check framework in Jerry uses the stack pointer to calculate the stack depth. This way of stack depth calculation is affected by the tail call optimalization (as the stack does not increase). By disabling the tail call optimalization at given points the stack limit calculation will work as expected. This causes a bit of stack overhead, but the Proxy in it self is a fairly big chunk of code and this stack limit would only be relevant if the Proxy already does recusion which already very edge case. The stack limit (--stack-limit=..) should be enabled to correctly report such stack depth errors. JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.usz@partner.samsung.com
This commit is contained in:
@@ -54,8 +54,7 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# FIXME: regression-test-issue-3785.js currently are deadloop on OSX.
|
- run: $RUNNER -q --jerry-tests
|
||||||
- run: $RUNNER -q --jerry-tests --skip-list=regression-test-issue-3785.js
|
|
||||||
- run: $RUNNER -q --unittests
|
- run: $RUNNER -q --unittests
|
||||||
|
|
||||||
OSX_x86-64_Build_Correctness_Unit_Tests_Debug:
|
OSX_x86-64_Build_Correctness_Unit_Tests_Debug:
|
||||||
@@ -152,7 +151,7 @@ jobs:
|
|||||||
- run: >-
|
- run: >-
|
||||||
$RUNNER -q --jerry-tests
|
$RUNNER -q --jerry-tests
|
||||||
--buildoptions=--stack-limit=0,--compile-flag=-fsanitize=address,--compile-flag=-m32,--compile-flag=-fno-omit-frame-pointer,--compile-flag=-fno-common,--compile-flag=-O2,--debug,--system-allocator=on,--linker-flag=-fuse-ld=gold
|
--buildoptions=--stack-limit=0,--compile-flag=-fsanitize=address,--compile-flag=-m32,--compile-flag=-fno-omit-frame-pointer,--compile-flag=-fno-common,--compile-flag=-O2,--debug,--system-allocator=on,--linker-flag=-fuse-ld=gold
|
||||||
--skip-list=parser-oom.js,parser-oom2.js,stack-limit.js,regression-test-issue-2190.js,regression-test-issue-2258-2963.js,regression-test-issue-2448.js,regression-test-issue-2905.js,regression-test-issue-3785.js
|
--skip-list=parser-oom.js,parser-oom2.js,stack-limit.js,regression-test-issue-2190.js,regression-test-issue-2258-2963.js,regression-test-issue-2448.js,regression-test-issue-2905.js,regression-test-issue-3785.js,proxy-evil-recursion.js
|
||||||
|
|
||||||
ASAN_Tests_Debug:
|
ASAN_Tests_Debug:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -165,7 +164,7 @@ jobs:
|
|||||||
- run: >-
|
- run: >-
|
||||||
$RUNNER -q --jerry-tests --build-debug
|
$RUNNER -q --jerry-tests --build-debug
|
||||||
--buildoptions=--stack-limit=0,--compile-flag=-fsanitize=address,--compile-flag=-m32,--compile-flag=-fno-omit-frame-pointer,--compile-flag=-fno-common,--compile-flag=-O2,--debug,--system-allocator=on,--linker-flag=-fuse-ld=gold
|
--buildoptions=--stack-limit=0,--compile-flag=-fsanitize=address,--compile-flag=-m32,--compile-flag=-fno-omit-frame-pointer,--compile-flag=-fno-common,--compile-flag=-O2,--debug,--system-allocator=on,--linker-flag=-fuse-ld=gold
|
||||||
--skip-list=parser-oom.js,parser-oom2.js,stack-limit.js,regression-test-issue-2190.js,regression-test-issue-2258-2963.js,regression-test-issue-2448.js,regression-test-issue-2905.js,regression-test-issue-3785.js
|
--skip-list=parser-oom.js,parser-oom2.js,stack-limit.js,regression-test-issue-2190.js,regression-test-issue-2258-2963.js,regression-test-issue-2448.js,regression-test-issue-2905.js,regression-test-issue-3785.js,proxy-evil-recursion.js
|
||||||
|
|
||||||
UBSAN_Tests:
|
UBSAN_Tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -2242,16 +2242,29 @@ typedef struct
|
|||||||
#if (JERRY_STACK_LIMIT != 0)
|
#if (JERRY_STACK_LIMIT != 0)
|
||||||
/**
|
/**
|
||||||
* Check the current stack usage. If the limit is reached a RangeError is raised.
|
* Check the current stack usage. If the limit is reached a RangeError is raised.
|
||||||
|
* The macro argument specifies the return value which is usally ECMA_VALUE_ERROR or NULL.
|
||||||
*/
|
*/
|
||||||
#define ECMA_CHECK_STACK_USAGE() \
|
#define ECMA_CHECK_STACK_USAGE_RETURN(RETURN_VALUE) \
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
if (ecma_get_current_stack_usage () > CONFIG_MEM_STACK_LIMIT) \
|
if (ecma_get_current_stack_usage () > CONFIG_MEM_STACK_LIMIT) \
|
||||||
{ \
|
{ \
|
||||||
return ecma_raise_range_error (ECMA_ERR_MSG ("Maximum call stack size exceeded")); \
|
ecma_raise_range_error (ECMA_ERR_MSG ("Maximum call stack size exceeded")); \
|
||||||
|
return RETURN_VALUE; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized version of ECMA_CHECK_STACK_USAGE_RETURN which returns ECMA_VALUE_ERROR.
|
||||||
|
* This version should be used in most cases.
|
||||||
|
*/
|
||||||
|
#define ECMA_CHECK_STACK_USAGE() ECMA_CHECK_STACK_USAGE_RETURN(ECMA_VALUE_ERROR)
|
||||||
#else /* JERRY_STACK_LIMIT == 0) */
|
#else /* JERRY_STACK_LIMIT == 0) */
|
||||||
|
/**
|
||||||
|
* If the stack limit is unlimited, this check is an empty macro.
|
||||||
|
*/
|
||||||
|
#define ECMA_CHECK_STACK_USAGE_RETURN(RETURN_VALUE)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the stack limit is unlimited, this check is an empty macro.
|
* If the stack limit is unlimited, this check is an empty macro.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ ecma_value_t
|
|||||||
ecma_proxy_object_get_prototype_of (ecma_object_t *obj_p) /**< proxy object */
|
ecma_proxy_object_get_prototype_of (ecma_object_t *obj_p) /**< proxy object */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -284,7 +285,9 @@ ecma_proxy_object_get_prototype_of (ecma_object_t *obj_p) /**< proxy object */
|
|||||||
/* 7. */
|
/* 7. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_builtin_object_object_get_prototype_of (target_obj_p);
|
ecma_value_t result = ecma_builtin_object_object_get_prototype_of (target_obj_p);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -373,6 +376,7 @@ ecma_proxy_object_set_prototype_of (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
ecma_value_t proto) /**< new prototype object */
|
ecma_value_t proto) /**< new prototype object */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
/* 1. */
|
/* 1. */
|
||||||
JERRY_ASSERT (ecma_is_value_object (proto) || ecma_is_value_null (proto));
|
JERRY_ASSERT (ecma_is_value_object (proto) || ecma_is_value_null (proto));
|
||||||
@@ -399,10 +403,14 @@ ecma_proxy_object_set_prototype_of (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
{
|
{
|
||||||
if (ECMA_OBJECT_IS_PROXY (target_obj_p))
|
if (ECMA_OBJECT_IS_PROXY (target_obj_p))
|
||||||
{
|
{
|
||||||
return ecma_proxy_object_set_prototype_of (target_obj_p, proto);
|
ecma_value_t result = ecma_proxy_object_set_prototype_of (target_obj_p, proto);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ecma_op_ordinary_object_set_prototype_of (target_obj_p, proto);
|
ecma_value_t result = ecma_op_ordinary_object_set_prototype_of (target_obj_p, proto);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -488,6 +496,7 @@ ecma_value_t
|
|||||||
ecma_proxy_object_is_extensible (ecma_object_t *obj_p) /**< proxy object */
|
ecma_proxy_object_is_extensible (ecma_object_t *obj_p) /**< proxy object */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -509,7 +518,9 @@ ecma_proxy_object_is_extensible (ecma_object_t *obj_p) /**< proxy object */
|
|||||||
/* 7. */
|
/* 7. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_builtin_object_object_is_extensible (target_obj_p);
|
ecma_value_t result = ecma_builtin_object_object_is_extensible (target_obj_p);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -577,6 +588,7 @@ ecma_value_t
|
|||||||
ecma_proxy_object_prevent_extensions (ecma_object_t *obj_p) /**< proxy object */
|
ecma_proxy_object_prevent_extensions (ecma_object_t *obj_p) /**< proxy object */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -666,6 +678,8 @@ ecma_proxy_object_get_own_property_descriptor (ecma_object_t *obj_p, /**< proxy
|
|||||||
ecma_property_descriptor_t *prop_desc_p) /**< [out] property
|
ecma_property_descriptor_t *prop_desc_p) /**< [out] property
|
||||||
* descriptor */
|
* descriptor */
|
||||||
{
|
{
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
/* 2. */
|
/* 2. */
|
||||||
@@ -687,7 +701,9 @@ ecma_proxy_object_get_own_property_descriptor (ecma_object_t *obj_p, /**< proxy
|
|||||||
/* 8. */
|
/* 8. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, prop_desc_p);
|
ecma_value_t result = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, prop_desc_p);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -868,6 +884,7 @@ ecma_proxy_object_define_own_property (ecma_object_t *obj_p, /**< proxy object *
|
|||||||
const ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */
|
const ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -889,7 +906,9 @@ ecma_proxy_object_define_own_property (ecma_object_t *obj_p, /**< proxy object *
|
|||||||
/* 8. */
|
/* 8. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_op_object_define_own_property (target_obj_p, prop_name_p, prop_desc_p);
|
ecma_value_t result = ecma_op_object_define_own_property (target_obj_p, prop_name_p, prop_desc_p);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 9. */
|
/* 9. */
|
||||||
@@ -1050,7 +1069,9 @@ ecma_proxy_object_has (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
/* 8. */
|
/* 8. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_op_object_has_property (target_obj_p, prop_name_p);
|
ecma_value_t result = ecma_op_object_has_property (target_obj_p, prop_name_p);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -1153,7 +1174,9 @@ ecma_proxy_object_get (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
ecma_object_t *target_obj_p = ecma_get_object_from_value (proxy_obj_p->target);
|
ecma_object_t *target_obj_p = ecma_get_object_from_value (proxy_obj_p->target);
|
||||||
return ecma_op_object_get_with_receiver (target_obj_p, prop_name_p, receiver);
|
ecma_value_t result = ecma_op_object_get_with_receiver (target_obj_p, prop_name_p, receiver);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -1259,7 +1282,9 @@ ecma_proxy_object_set (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
/* 8. */
|
/* 8. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_op_object_put_with_receiver (target_obj_p, prop_name_p, value, receiver, is_strict);
|
ecma_value_t result = ecma_op_object_put_with_receiver (target_obj_p, prop_name_p, value, receiver, is_strict);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -1357,6 +1382,7 @@ ecma_proxy_object_delete_property (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
bool is_strict) /**< delete in strict mode? */
|
bool is_strict) /**< delete in strict mode? */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -1378,7 +1404,9 @@ ecma_proxy_object_delete_property (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
/* 8. */
|
/* 8. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_op_object_delete (target_obj_p, prop_name_p, is_strict);
|
ecma_value_t result = ecma_op_object_delete (target_obj_p, prop_name_p, is_strict);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -1579,6 +1607,7 @@ ecma_collection_t *
|
|||||||
ecma_proxy_object_own_property_keys (ecma_object_t *obj_p) /**< proxy object */
|
ecma_proxy_object_own_property_keys (ecma_object_t *obj_p) /**< proxy object */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE_RETURN (NULL);
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -1599,7 +1628,9 @@ ecma_proxy_object_own_property_keys (ecma_object_t *obj_p) /**< proxy object */
|
|||||||
/* 6. */
|
/* 6. */
|
||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
return ecma_op_object_own_property_keys (target_obj_p);
|
ecma_collection_t *result = ecma_op_object_own_property_keys (target_obj_p);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||||
@@ -1740,6 +1771,7 @@ ecma_proxy_object_call (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
uint32_t argc) /**< number of arguments */
|
uint32_t argc) /**< number of arguments */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -1761,7 +1793,9 @@ ecma_proxy_object_call (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
if (ecma_is_value_undefined (trap))
|
if (ecma_is_value_undefined (trap))
|
||||||
{
|
{
|
||||||
ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
|
ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
|
||||||
return ecma_op_function_call (target_obj_p, this_argument, args_p, argc);
|
ecma_value_t result = ecma_op_function_call (target_obj_p, this_argument, args_p, argc);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 8. */
|
/* 8. */
|
||||||
@@ -1794,6 +1828,7 @@ ecma_proxy_object_construct (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
uint32_t argc) /**< number of arguments */
|
uint32_t argc) /**< number of arguments */
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||||
|
ECMA_CHECK_STACK_USAGE ();
|
||||||
|
|
||||||
ecma_proxy_object_t * proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
ecma_proxy_object_t * proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||||
|
|
||||||
@@ -1817,7 +1852,9 @@ ecma_proxy_object_construct (ecma_object_t *obj_p, /**< proxy object */
|
|||||||
{
|
{
|
||||||
JERRY_ASSERT (ecma_object_is_constructor (target_obj_p));
|
JERRY_ASSERT (ecma_object_is_constructor (target_obj_p));
|
||||||
|
|
||||||
return ecma_op_function_construct (target_obj_p, new_target_p, args_p, argc);
|
ecma_value_t result = ecma_op_function_construct (target_obj_p, new_target_p, args_p, argc);
|
||||||
|
JERRY_BLOCK_TAIL_CALL_OPTIMIZATION ();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 8. */
|
/* 8. */
|
||||||
|
|||||||
@@ -153,4 +153,37 @@ void JERRY_ATTR_NORETURN jerry_fatal (jerry_fatal_code_t code);
|
|||||||
#define JERRY__LOG2_8(n) (((n) >= 1 << 8) ? (8 + JERRY__LOG2_4 ((n) >> 8)) : JERRY__LOG2_4 (n))
|
#define JERRY__LOG2_8(n) (((n) >= 1 << 8) ? (8 + JERRY__LOG2_4 ((n) >> 8)) : JERRY__LOG2_4 (n))
|
||||||
#define JERRY_LOG2(n) (((n) >= 1 << 16) ? (16 + JERRY__LOG2_8 ((n) >> 16)) : JERRY__LOG2_8 (n))
|
#define JERRY_LOG2(n) (((n) >= 1 << 16) ? (16 + JERRY__LOG2_8 ((n) >> 16)) : JERRY__LOG2_8 (n))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JERRY_BLOCK_TAIL_CALL_OPTIMIZATION
|
||||||
|
*
|
||||||
|
* Adapted from abseil ( https://github.com/abseil/ )
|
||||||
|
*
|
||||||
|
* Instructs the compiler to avoid optimizing tail-call recursion. This macro is
|
||||||
|
* useful when you wish to preserve the existing function order within a stack
|
||||||
|
* trace for logging, debugging, or profiling purposes.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* int f() {
|
||||||
|
* int result = g();
|
||||||
|
* JERRY_BLOCK_TAIL_CALL_OPTIMIZATION();
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* This macro is intentionally here as jerryscript-compiler.h is a public header and
|
||||||
|
* it does not make sense to expose this macro to the public.
|
||||||
|
*/
|
||||||
|
#if defined (__clang__) || defined (__GNUC__)
|
||||||
|
/* Clang/GCC will not tail call given inline volatile assembly. */
|
||||||
|
#define JERRY_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__ ("")
|
||||||
|
#else /* !defined(__clang__) && !defined(__GNUC__) */
|
||||||
|
/* On GCC 10.x this version also works. */
|
||||||
|
#define JERRY_BLOCK_TAIL_CALL_OPTIMIZATION() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
JERRY_CONTEXT (status_flags) |= ECMA_STATUS_API_AVAILABLE; \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
#endif /* defined(__clang__) || defined (__GNUC__) */
|
||||||
|
|
||||||
#endif /* !JRT_H */
|
#endif /* !JRT_H */
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
/* Stack limit check must be enabled to run this test correctly! */
|
||||||
|
|
||||||
|
function expect_error(error_type, method) {
|
||||||
|
try {
|
||||||
|
method();
|
||||||
|
assert(false);
|
||||||
|
} catch (ex) {
|
||||||
|
assert (ex instanceof error_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a proxy with recursion via prototype
|
||||||
|
*/
|
||||||
|
var proxy_one = new Proxy({length:2}, {});
|
||||||
|
|
||||||
|
Reflect.setPrototypeOf(proxy_one, proxy_one); /* [[SetPrototypeOf]] does not trigger recursion */
|
||||||
|
// aka. proxy_one.__proto__ = proxy_one; /* [[SetPrototypeOf]] */
|
||||||
|
|
||||||
|
function test_proxy_internals_using_proto(proxy_obj) {
|
||||||
|
/* These methods can be recursive calls via prototype */
|
||||||
|
expect_error(RangeError, function() { proxy_obj[1]; /* [[Get]] */ });
|
||||||
|
expect_error(RangeError, function() { proxy_obj[1] = 2; /* [[Set]] */ });
|
||||||
|
expect_error(RangeError, function() { 3 in proxy_obj; /* [[Has]] */ });
|
||||||
|
expect_error(RangeError, function() { Reflect.has(proxy_obj, 3); /* [[Has]] */ });
|
||||||
|
}
|
||||||
|
|
||||||
|
test_proxy_internals_using_proto(proxy_one);
|
||||||
|
|
||||||
|
function test_proxy_internals_not_using_proto(proxy_obj) {
|
||||||
|
/* The methods below should not trigger recursion via __proto__ */
|
||||||
|
delete proxy_obj.none; /* [[Delete]] */
|
||||||
|
delete proxy_obj["other"]; /* [[Delete]] */
|
||||||
|
|
||||||
|
Object.getPrototypeOf(proxy_obj); /* [[GetPrototypeOf]] */
|
||||||
|
Reflect.isExtensible(proxy_obj); /* [[IsExtensible]] */
|
||||||
|
Object.preventExtensions(proxy_obj); /* [[PreventExtensions]] */
|
||||||
|
Reflect.getOwnPropertyDescriptor(proxy_obj, "key"); /* [[GetOwnProperty]] */
|
||||||
|
Reflect.defineProperty(proxy_obj, "key2", { value: 4}); /* [[DefineOwnProperty]] */
|
||||||
|
Reflect.ownKeys(proxy_obj); /* [[OwnPropertyKeys]] */
|
||||||
|
}
|
||||||
|
|
||||||
|
test_proxy_internals_not_using_proto(proxy_one);
|
||||||
|
|
||||||
|
expect_error(TypeError, function() { proxy_one(3, 4); /* [[Call]] */ });
|
||||||
|
expect_error(TypeError, function() { new proxy_one(4); /* [[Construct]] */ });
|
||||||
|
|
||||||
|
/* Special handler to trigger proxy recursion, WARNING DO NOT use in production code! */
|
||||||
|
var handler = {
|
||||||
|
is_handler: true,
|
||||||
|
proxy_target: false,
|
||||||
|
counts: {
|
||||||
|
get: 0,
|
||||||
|
set: 0,
|
||||||
|
has: 0,
|
||||||
|
deleteProperty: 0,
|
||||||
|
getPrototypeOf: 0,
|
||||||
|
setPrototypeOf: 0,
|
||||||
|
isExtensible: 0,
|
||||||
|
preventExtensions: 0,
|
||||||
|
getOwnPropertyDescriptor: 0,
|
||||||
|
defineProperty: 0,
|
||||||
|
ownKeys: 0,
|
||||||
|
},
|
||||||
|
get: function(target, key) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.get++;
|
||||||
|
return this.proxy_target.key;
|
||||||
|
},
|
||||||
|
set: function(target, key, value) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.set++;
|
||||||
|
return this.proxy_target.key = value;
|
||||||
|
},
|
||||||
|
has: function(target, key) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.has++;
|
||||||
|
return Reflect.has(this.proxy_target, key);
|
||||||
|
},
|
||||||
|
deleteProperty: function(target, key) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.deleteProperty++;
|
||||||
|
return Reflect.deleteProperty(this.proxy_target, key);
|
||||||
|
},
|
||||||
|
getPrototypeOf: function(target) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.getPrototypeOf++;
|
||||||
|
return Reflect.getPrototypeOf(this.proxy_target);
|
||||||
|
},
|
||||||
|
setPrototypeOf: function(target, newproto) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.setPrototypeOf++;
|
||||||
|
return Reflect.setPrototypeOf(this.proxy_target, newproto);
|
||||||
|
},
|
||||||
|
isExtensible: function(target) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.isExtensible++;
|
||||||
|
return Reflect.isExtensible(this.proxy_target);
|
||||||
|
},
|
||||||
|
preventExtensions: function(target) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.preventExtensions++;
|
||||||
|
return Reflect.preventExtensions(this.proxy_target);
|
||||||
|
},
|
||||||
|
getOwnPropertyDescriptor: function(target, key) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.getOwnPropertyDescriptor++;
|
||||||
|
return Reflect.getOwnPropertyDescriptor(this.proxy_target, key);
|
||||||
|
},
|
||||||
|
defineProperty: function(target, key, attrs) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.defineProperty++;
|
||||||
|
return Reflect.defineProperty(this.proxy_target, key, attrs);
|
||||||
|
},
|
||||||
|
ownKeys: function(target) {
|
||||||
|
assert(this.is_handler);
|
||||||
|
assert(this.proxy_target !== false);
|
||||||
|
this.counts.ownKeys++;
|
||||||
|
return Reflect.ownKeys(this.proxy_target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var proxy_two = new Proxy({length:2}, handler);
|
||||||
|
|
||||||
|
handler.proxy_target = proxy_two;
|
||||||
|
|
||||||
|
test_proxy_internals_using_proto(proxy_two);
|
||||||
|
|
||||||
|
expect_error(RangeError, function() { delete proxy_two.none; /* [[Delete]] */ });
|
||||||
|
expect_error(RangeError, function() { delete proxy_two["other"]; /* [[Delete]] */ });
|
||||||
|
expect_error(RangeError, function() { Object.getPrototypeOf(proxy_two); /* [[GetPrototypeOf]] */ });
|
||||||
|
expect_error(RangeError, function() { Object.setPrototypeOf(proxy_two, {}); /* [[GetPrototypeOf]] */ });
|
||||||
|
expect_error(RangeError, function() { Reflect.isExtensible(proxy_two); /* [[IsExtensible]] */ });
|
||||||
|
expect_error(RangeError, function() { Object.preventExtensions(proxy_two); /* [[PreventExtensions]] */ });
|
||||||
|
expect_error(RangeError, function() { Reflect.getOwnPropertyDescriptor(proxy_two, "key"); /* [[GetOwnProperty]] */ });
|
||||||
|
expect_error(RangeError, function() { Reflect.defineProperty(proxy_two, "key2", { value: 4}); /* [[DefineOwnProperty]] */ });
|
||||||
|
expect_error(RangeError, function() { Reflect.ownKeys(proxy_two); /* [[OwnPropertyKeys]] */ });
|
||||||
Reference in New Issue
Block a user