diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 4ece159e8..0e013c1fb 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -685,6 +685,12 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ } #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + /** + * Have the object's prototype here so the object could set it to JMEM_CP_NULL + * if the prototype should be ignored (like in case of PROXY). + */ + jmem_cpointer_t proto_cp = object_p->u2.prototype_cp; + switch (object_type) { #if !ENABLED (JERRY_BUILTIN_REALMS) @@ -852,8 +858,6 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ } } - jmem_cpointer_t proto_cp = object_p->u2.prototype_cp; - if (proto_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp)); @@ -866,18 +870,11 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ case ECMA_OBJECT_TYPE_PROXY: { ecma_gc_mark_proxy_object (object_p); - /* No need to free the properties of a proxy (there should be none). - * Aside from the tag bits every other bit should be zero, - */ - JERRY_ASSERT ((object_p->u1.property_list_cp & ~JMEM_TAG_MASK) == 0); - - jmem_cpointer_t proto_cp = object_p->u2.prototype_cp; - - if (proto_cp != JMEM_CP_NULL) - { - ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp)); - } - return; + /* The protoype of the proxy should be "empty" (aside from the tag bits every other bit should be zero). */ + JERRY_ASSERT ((object_p->u2.prototype_cp & ~JMEM_TAG_MASK) == 0); + /* Make sure that the prototype is not checked below. */ + proto_cp = JMEM_CP_NULL; + break; } #endif /* ENABLED (JERRY_BUILTIN_PROXY) */ case ECMA_OBJECT_TYPE_BOUND_FUNCTION: @@ -1033,8 +1030,6 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ } } - jmem_cpointer_t proto_cp = object_p->u2.prototype_cp; - if (proto_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp)); @@ -1649,12 +1644,10 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ #if ENABLED (JERRY_BUILTIN_PROXY) case ECMA_OBJECT_TYPE_PROXY: { - /* No need to free the properties of a proxy (there should be none). - * Aside from the tag bits every other bit should be zero, - */ - JERRY_ASSERT ((object_p->u1.property_list_cp & ~JMEM_TAG_MASK) == 0); - ecma_dealloc_extended_object (object_p, sizeof (ecma_proxy_object_t)); - return; + /* The protoype of the proxy should be "empty" (aside from the tag bits every other bit should be zero). */ + JERRY_ASSERT ((object_p->u2.prototype_cp & ~JMEM_TAG_MASK) == 0); + ext_object_size = sizeof (ecma_proxy_object_t); + break; } #endif /* ENABLED (JERRY_BUILTIN_PROXY) */ case ECMA_OBJECT_TYPE_FUNCTION: diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 1b429b873..7b5cfac12 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -2166,8 +2166,8 @@ do \ * Description of Proxy objects. * * A Proxy object's property list is used to store extra information: - * * The "header.u1.property_list_cp" 1st tag bit stores the IsCallable information. - * * The "header.u1.property_list_cp" 2nd tag bit stores the IsConstructor information. + * * The "header.u2.prototype_cp" 1st tag bit stores the IsCallable information. + * * The "header.u2.prototype_cp" 2nd tag bit stores the IsConstructor information. */ typedef struct { diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index b6069ac1d..117ca7c7d 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -106,7 +106,7 @@ ecma_op_object_is_callable (ecma_object_t *obj_p) /**< ecma object */ #if ENABLED (JERRY_BUILTIN_PROXY) if (ECMA_OBJECT_TYPE_IS_PROXY (type)) { - return ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (obj_p->u1.property_list_cp) != 0; + return ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (obj_p->u2.prototype_cp) != 0; } #endif /* ENABLED (JERRY_BUILTIN_PROXY) */ @@ -215,7 +215,7 @@ ecma_object_check_constructor (ecma_object_t *obj_p) /**< ecma object */ #if ENABLED (JERRY_BUILTIN_PROXY) if (ECMA_OBJECT_TYPE_IS_PROXY (type)) { - if (ECMA_GET_SECOND_BIT_FROM_POINTER_TAG (obj_p->u1.property_list_cp) == 0) + if (ECMA_GET_SECOND_BIT_FROM_POINTER_TAG (obj_p->u2.prototype_cp) == 0) { return ECMA_ERR_MSG ("Proxy target is not a constructor."); } diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c b/jerry-core/ecma/operations/ecma-proxy-object.c index 31d585666..d18af45eb 100644 --- a/jerry-core/ecma/operations/ecma-proxy-object.c +++ b/jerry-core/ecma/operations/ecma-proxy-object.c @@ -60,9 +60,8 @@ ecma_proxy_create (ecma_value_t target, /**< proxy target */ /* ES2015: 5 - 6. */ /* ES11+: 3 - 4. */ - ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE), - sizeof (ecma_proxy_object_t), - ECMA_OBJECT_TYPE_PROXY); + /* A Proxy does not have [[Prototype]] value as per standard */ + ecma_object_t *obj_p = ecma_create_object (NULL, sizeof (ecma_proxy_object_t), ECMA_OBJECT_TYPE_PROXY); ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; @@ -70,13 +69,13 @@ ecma_proxy_create (ecma_value_t target, /**< proxy target */ /* ES11+: 5. */ if (ecma_op_is_callable (target)) { - ECMA_SET_FIRST_BIT_TO_POINTER_TAG (obj_p->u1.property_list_cp); + ECMA_SET_FIRST_BIT_TO_POINTER_TAG (obj_p->u2.prototype_cp); /* ES2015: 7.b. */ /* ES11+: 5.b. */ if (ecma_is_constructor (target)) { - ECMA_SET_SECOND_BIT_TO_POINTER_TAG (obj_p->u1.property_list_cp); + ECMA_SET_SECOND_BIT_TO_POINTER_TAG (obj_p->u2.prototype_cp); } } diff --git a/tests/jerry/es.next/regression-test-issue-4396.js b/tests/jerry/es.next/regression-test-issue-4396.js new file mode 100644 index 000000000..a1c00e6d1 --- /dev/null +++ b/tests/jerry/es.next/regression-test-issue-4396.js @@ -0,0 +1,22 @@ +// 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() { +} + +var proxy = new Proxy(demo, {}); +var weakset = new WeakSet(); + +/* Internal: WeakSet adds an extra internal property to the proxy object. */ +weakset.add(proxy); diff --git a/tests/unit-core/test-proxy.c b/tests/unit-core/test-proxy.c index 6346387a7..029c14ad9 100644 --- a/tests/unit-core/test-proxy.c +++ b/tests/unit-core/test-proxy.c @@ -148,6 +148,79 @@ set_function (jerry_value_t target, /**< target object */ jerry_release_value (function_val); } /* set_function */ +struct test_data +{ + int value; +}; + +static void +proxy_native_freecb (void *user_p) +{ + TEST_ASSERT (user_p != NULL); + struct test_data *native_p = (struct test_data *) user_p; + native_p->value = -1; +} /* proxy_native_freecb */ + +static const jerry_object_native_info_t proxy_native_info = +{ + .free_cb = proxy_native_freecb, +}; + +static jerry_value_t +proxy_native_handler_get (const jerry_value_t function_obj, /**< function object */ + const jerry_value_t this_val, /**< this arg */ + const jerry_value_t args_p[], /**< function arguments */ + const jerry_length_t args_count) /**< number of function arguments */ +{ + JERRY_UNUSED (function_obj); + JERRY_UNUSED (this_val); + TEST_ASSERT (args_count == 3); + + /* 3rd argument (Receiver) should be the Proxy here. */ + jerry_value_t receiver = args_p[2]; + TEST_ASSERT (jerry_value_is_proxy (receiver)); + + /* Check if proxy has the native ptr. */ + struct test_data *native_p; + bool has_p = jerry_get_object_native_pointer (receiver, (void *) &native_p, &proxy_native_info); + TEST_ASSERT (has_p == true); + + native_p->value <<= 1; + return jerry_create_number (native_p->value); +} /* proxy_native_handler_get */ + +/** + * Test Proxy with added native object. + */ +static void +test_proxy_native (void) +{ + jerry_value_t handler = jerry_create_object (); + set_function (handler, "get", proxy_native_handler_get); + + jerry_value_t target = jerry_create_object (); + jerry_value_t proxy = jerry_create_proxy (target, handler); + + struct test_data *data = (struct test_data *) malloc (sizeof (struct test_data)); + data->value = 2; + jerry_set_object_native_pointer (proxy, data, &proxy_native_info); + + /* Call: proxy[10] */ + jerry_value_t result_for_10 = jerry_get_property_by_index (proxy, 10); + TEST_ASSERT (jerry_value_is_number (result_for_10)); + TEST_ASSERT (jerry_get_number_value (result_for_10) == 4.0); + + /* Call: proxy[5] */ + data->value = 8; + jerry_value_t result_for_5 = jerry_get_property_by_index (proxy, 5); + TEST_ASSERT (jerry_value_is_number (result_for_5)); + TEST_ASSERT (jerry_get_number_value (result_for_5) == 16.0); + + jerry_release_value (handler); + jerry_release_value (target); + jerry_release_value (proxy); +} /* test_proxy_native */ + int main (void) { @@ -292,6 +365,8 @@ main (void) jerry_release_value (proxy); jerry_release_value (target); + test_proxy_native (); + jerry_cleanup (); return 0; } /* main */