• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright JS Foundation and other contributors, http://js.foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecma-alloc.h"
17 #include "ecma-array-object.h"
18 #include "ecma-builtins.h"
19 #include "ecma-builtin-object.h"
20 #include "ecma-exceptions.h"
21 #include "ecma-function-object.h"
22 #include "ecma-gc.h"
23 #include "ecma-globals.h"
24 #include "ecma-helpers.h"
25 #include "ecma-objects.h"
26 #include "ecma-objects-general.h"
27 #include "ecma-proxy-object.h"
28 #include "jcontext.h"
29 
30 /** \addtogroup ecma ECMA
31  * @{
32  *
33  * \addtogroup ecmaproxyobject ECMA Proxy object related routines
34  * @{
35  */
36 
37 #if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
38 /**
39  * Check whether the argument satifies the requrements of [[ProxyTarget]] or [[ProxyHandler]]
40  *
41  * See also:
42  *          ES2015 9.5.15.1-2
43  *          ES2015 9.5.15.3-4
44  *
45  * @return true - if the arguments can be a valid [[ProxyTarget]] or [[ProxyHandler]]
46  *         false - otherwise
47  */
48 static bool
ecma_proxy_validate(ecma_value_t argument)49 ecma_proxy_validate (ecma_value_t argument) /**< argument to validate */
50 {
51   if (ecma_is_value_object (argument))
52   {
53     ecma_object_t *obj_p = ecma_get_object_from_value (argument);
54 
55     return (!ECMA_OBJECT_IS_PROXY (obj_p)
56             || !ecma_is_value_null (((ecma_proxy_object_t *) obj_p)->handler));
57   }
58 
59   return false;
60 } /* ecma_proxy_validate */
61 
62 /**
63  * ProxyCreate operation for create a new proxy object
64  *
65  * See also:
66  *         ES2015 9.5.15
67  *
68  * @return created Proxy object as an ecma-value - if success
69  *         raised error - otherwise
70  */
71 ecma_object_t *
ecma_proxy_create(ecma_value_t target,ecma_value_t handler)72 ecma_proxy_create (ecma_value_t target, /**< proxy target */
73                    ecma_value_t handler) /**< proxy handler */
74 {
75   /* 1 - 4. */
76   if (!ecma_proxy_validate (target) || !ecma_proxy_validate (handler))
77   {
78     ecma_raise_type_error (ECMA_ERR_MSG ("Cannot create proxy with a non-object target or handler"));
79     return NULL;
80   }
81 
82   /* 5 - 6. */
83   ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE),
84                                              sizeof (ecma_proxy_object_t),
85                                              ECMA_OBJECT_TYPE_PROXY);
86 
87   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
88 
89   /* 8. */
90   proxy_obj_p->target = target;
91   /* 9. */
92   proxy_obj_p->handler = handler;
93 
94   /* 10. */
95   return obj_p;
96 } /* ecma_proxy_create */
97 
98 /**
99  * Definition of Proxy Revocation Function
100  *
101  * See also:
102  *         ES2015 26.2.2.1.1
103  *
104  * @return ECMA_VALUE_UNDEFINED
105  */
106 ecma_value_t
ecma_proxy_revoke_cb(const ecma_value_t function_obj,const ecma_value_t this_val,const ecma_value_t args_p[],const ecma_length_t args_count)107 ecma_proxy_revoke_cb (const ecma_value_t function_obj, /**< the function itself */
108                       const ecma_value_t this_val, /**< this_arg of the function */
109                       const ecma_value_t args_p[], /**< argument list */
110                       const ecma_length_t args_count) /**< argument number */
111 {
112   JERRY_UNUSED_3 (this_val, args_p, args_count);
113 
114   ecma_object_t *func_obj_p = ecma_get_object_from_value (function_obj);
115 
116   /* 1. */
117   ecma_revocable_proxy_object_t *rev_proxy_p = (ecma_revocable_proxy_object_t *) func_obj_p;
118 
119   /* 2. */
120   if (ecma_is_value_null (rev_proxy_p->proxy))
121   {
122     return ECMA_VALUE_UNDEFINED;
123   }
124 
125   /* 4. */
126   ecma_proxy_object_t *proxy_p = (ecma_proxy_object_t *) ecma_get_object_from_value (rev_proxy_p->proxy);
127   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY ((ecma_object_t *) proxy_p));
128 
129   /* 3. */
130   rev_proxy_p->proxy = ECMA_VALUE_NULL;
131 
132   /* 5. */
133   proxy_p->target = ECMA_VALUE_NULL;
134 
135   /* 6. */
136   proxy_p->handler = ECMA_VALUE_NULL;
137 
138   /* 7. */
139   return ECMA_VALUE_UNDEFINED;
140 } /* ecma_proxy_revoke_cb */
141 
142 /**
143  * Proxy.revocable operation for create a new revocable proxy object
144  *
145  * See also:
146  *         ES2015 26.2.2.1
147  *
148  * @return NULL - if the operation fails
149  *         pointer to the newly created revocable proxy object - otherwise
150  */
151 ecma_object_t *
ecma_proxy_create_revocable(ecma_value_t target,ecma_value_t handler)152 ecma_proxy_create_revocable (ecma_value_t target, /**< target argument */
153                              ecma_value_t handler) /**< handler argument */
154 {
155   /* 1. */
156   ecma_object_t *proxy_p = ecma_proxy_create (target, handler);
157 
158   /* 2. */
159   if (proxy_p == NULL)
160   {
161     return proxy_p;
162   }
163 
164   ecma_value_t proxy_value = ecma_make_object_value (proxy_p);
165 
166   /* 3. */
167   ecma_object_t *func_obj_p;
168   func_obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE),
169                                    sizeof (ecma_revocable_proxy_object_t),
170                                    ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION);
171 
172   ecma_revocable_proxy_object_t *rev_proxy_p = (ecma_revocable_proxy_object_t *) func_obj_p;
173   rev_proxy_p->header.u.external_handler_cb = ecma_proxy_revoke_cb;
174   /* 4. */
175   rev_proxy_p->proxy = proxy_value;
176 
177   ecma_property_value_t *prop_value_p;
178   ecma_value_t revoker = ecma_make_object_value (func_obj_p);
179 
180   /* 5. */
181   ecma_object_t *obj_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE),
182                                              0,
183                                              ECMA_OBJECT_TYPE_GENERAL);
184 
185   /* 6. */
186   prop_value_p = ecma_create_named_data_property (obj_p,
187                                                   ecma_get_magic_string (LIT_MAGIC_STRING_PROXY),
188                                                   ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
189                                                   NULL);
190   prop_value_p->value = proxy_value;
191 
192   /* 7. */
193   prop_value_p = ecma_create_named_data_property (obj_p,
194                                                   ecma_get_magic_string (LIT_MAGIC_STRING_REVOKE),
195                                                   ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
196                                                   NULL);
197   prop_value_p->value = revoker;
198 
199   ecma_deref_object (proxy_p);
200   ecma_deref_object (func_obj_p);
201 
202   /* 8. */
203   return obj_p;
204 } /* ecma_proxy_create_revocable */
205 
206 /**
207  * Internal find property operation for Proxy object
208  *
209  * Note: Returned value must be freed with ecma_free_value.
210  *
211  * @return ECMA_VALUE_ERROR - if the operation fails
212  *         ECMA_VALUE_NOT_FOUND - if the property is not found
213  *         value of the property - otherwise
214  */
215 ecma_value_t
ecma_proxy_object_find(ecma_object_t * obj_p,ecma_string_t * prop_name_p)216 ecma_proxy_object_find (ecma_object_t *obj_p, /**< proxy object */
217                         ecma_string_t *prop_name_p) /**< property name */
218 {
219   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
220 
221   ecma_value_t has_result = ecma_proxy_object_has (obj_p, prop_name_p);
222 
223   if (ECMA_IS_VALUE_ERROR (has_result))
224   {
225     return has_result;
226   }
227 
228   if (ecma_is_value_false (has_result))
229   {
230     return ECMA_VALUE_NOT_FOUND;
231   }
232 
233   return ecma_proxy_object_get (obj_p, prop_name_p, ecma_make_object_value (obj_p));
234 } /* ecma_proxy_object_find */
235 
236 /**
237  * Convert the result of the ecma_proxy_object_get_prototype_of to compressed pointer
238  *
239  * Note: if `proto` is non-null, the reference from the object is released
240  *
241  * @return compressed pointer to the `proto` value
242  */
243 jmem_cpointer_t
ecma_proxy_object_prototype_to_cp(ecma_value_t proto)244 ecma_proxy_object_prototype_to_cp (ecma_value_t proto) /**< ECMA_VALUE_NULL or object */
245 {
246   JERRY_ASSERT (ecma_is_value_null (proto) || ecma_is_value_object (proto));
247 
248   if (ecma_is_value_null (proto))
249   {
250     return JMEM_CP_NULL;
251   }
252 
253   jmem_cpointer_t proto_cp;
254   ecma_object_t *proto_obj_p = ecma_get_object_from_value (proto);
255   ECMA_SET_POINTER (proto_cp, proto_obj_p);
256   ecma_deref_object (proto_obj_p);
257 
258   return proto_cp;
259 } /* ecma_proxy_object_prototype_to_cp */
260 
261 /**
262  * Helper method for validate the proxy object
263  *
264  * @return proxy trap - if the validation is successful
265  *         ECMA_VALUE_ERROR - otherwise
266  */
267 static ecma_value_t
ecma_validate_proxy_object(ecma_value_t handler,lit_magic_string_id_t magic_id)268 ecma_validate_proxy_object (ecma_value_t handler, /**< proxy handler */
269                             lit_magic_string_id_t magic_id) /**< routine magic id */
270 {
271   if (ecma_is_value_null (handler))
272   {
273     return ecma_raise_type_error (ECMA_ERR_MSG ("Handler can not be null"));
274   }
275 
276   JERRY_ASSERT (ecma_is_value_object (handler));
277 
278   return ecma_op_get_method_by_magic_id (handler, magic_id);
279 } /* ecma_validate_proxy_object */
280 
281 /* Interal operations */
282 
283 /**
284  * The Proxy object [[GetPrototypeOf]] internal routine
285  *
286  * See also:
287  *          ECMAScript v6, 9.5.1
288  *
289  * @return ECMA_VALUE_ERROR - if the operation fails
290  *         ECMA_VALUE_NULL or valid object (prototype) otherwise
291  */
292 ecma_value_t
ecma_proxy_object_get_prototype_of(ecma_object_t * obj_p)293 ecma_proxy_object_get_prototype_of (ecma_object_t *obj_p) /**< proxy object */
294 {
295   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
296 
297   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
298 
299   /* 1. */
300   ecma_value_t handler = proxy_obj_p->handler;
301 
302   /* 2-5. */
303   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_GET_PROTOTYPE_OF_UL);
304 
305   /* 6. */
306   if (ECMA_IS_VALUE_ERROR (trap))
307   {
308     return trap;
309   }
310 
311   ecma_value_t target = proxy_obj_p->target;
312   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
313 
314   /* 7. */
315   if (ecma_is_value_undefined (trap))
316   {
317     return ecma_builtin_object_object_get_prototype_of (target_obj_p);
318   }
319 
320   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
321 
322   /* 8. */
323   ecma_value_t handler_proto = ecma_op_function_call (func_obj_p, handler, &target, 1);
324 
325   ecma_deref_object (func_obj_p);
326 
327   /* 9. */
328   if (ECMA_IS_VALUE_ERROR (handler_proto))
329   {
330     return handler_proto;
331   }
332 
333   /* 10. */
334   if (!ecma_is_value_object (handler_proto) && !ecma_is_value_null (handler_proto))
335   {
336     ecma_free_value (handler_proto);
337 
338     return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned neither object nor null."));
339   }
340 
341   /* 11. */
342   ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
343 
344   /* 12. */
345   if (ECMA_IS_VALUE_ERROR (extensible_target))
346   {
347     ecma_free_value (handler_proto);
348 
349     return extensible_target;
350   }
351 
352   /* 13. */
353   if (ecma_is_value_true (extensible_target))
354   {
355     return handler_proto;
356   }
357 
358   /* 14. */
359   ecma_value_t target_proto = ecma_builtin_object_object_get_prototype_of (target_obj_p);
360 
361   /* 15. */
362   if (ECMA_IS_VALUE_ERROR (target_proto))
363   {
364     return target_proto;
365   }
366 
367   ecma_value_t ret_value = handler_proto;
368 
369   /* 16. */
370   if (handler_proto != target_proto)
371   {
372     ecma_free_value (handler_proto);
373 
374     ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Proxy target is non-extensible, but the trap did not "
375                                                      "return its actual prototype."));
376   }
377 
378   ecma_free_value (target_proto);
379 
380   /* 17. */
381   return ret_value;
382 } /* ecma_proxy_object_get_prototype_of */
383 
384 /**
385  * The Proxy object [[SetPrototypeOf]] internal routine
386  *
387  * See also:
388  *          ECMAScript v6, 9.5.2
389  *
390  * Note: Returned value is always a simple value so freeing it is unnecessary.
391  *
392  * @return ECMA_VALUE_ERROR - if the operation fails
393  *         ECMA_VALUE_{TRUE/FALSE} - depends on whether the new prototype can be set for the given object
394  */
395 ecma_value_t
ecma_proxy_object_set_prototype_of(ecma_object_t * obj_p,ecma_value_t proto)396 ecma_proxy_object_set_prototype_of (ecma_object_t *obj_p, /**< proxy object */
397                                     ecma_value_t proto) /**< new prototype object */
398 {
399   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
400 
401   /* 1. */
402   JERRY_ASSERT (ecma_is_value_object (proto) || ecma_is_value_null (proto));
403 
404   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
405 
406   /* 2. */
407   ecma_value_t handler = proxy_obj_p->handler;
408 
409   /* 3-6. */
410   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_SET_PROTOTYPE_OF_UL);
411 
412   /* 7.*/
413   if (ECMA_IS_VALUE_ERROR (trap))
414   {
415     return trap;
416   }
417 
418   ecma_value_t target = proxy_obj_p->target;
419   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
420 
421   /* 8. */
422   if (ecma_is_value_undefined (trap))
423   {
424     if (ECMA_OBJECT_IS_PROXY (target_obj_p))
425     {
426       return ecma_proxy_object_set_prototype_of (target_obj_p, proto);
427     }
428 
429     return ecma_op_ordinary_object_set_prototype_of (target_obj_p, proto);
430   }
431 
432   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
433   ecma_value_t args[] = { target, proto };
434 
435   /* 9. */
436   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2);
437 
438   ecma_deref_object (func_obj_p);
439 
440   /* 10. */
441   if (ECMA_IS_VALUE_ERROR (trap_result))
442   {
443     return trap_result;
444   }
445 
446   bool boolean_trap_result = ecma_op_to_boolean (trap_result);
447 
448   ecma_free_value (trap_result);
449 
450   /* 11. */
451   ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
452 
453   /* 12. */
454   if (ECMA_IS_VALUE_ERROR (extensible_target))
455   {
456     return extensible_target;
457   }
458 
459   /* 13. */
460   if (ecma_is_value_true (extensible_target))
461   {
462     return ecma_make_boolean_value (boolean_trap_result);
463   }
464 
465   /* 14. */
466   ecma_value_t target_proto = ecma_builtin_object_object_get_prototype_of (target_obj_p);
467 
468   /* 15. */
469   if (ECMA_IS_VALUE_ERROR (target_proto))
470   {
471     return target_proto;
472   }
473 
474   ecma_value_t ret_value = ecma_make_boolean_value (boolean_trap_result);
475 
476   /* 16. */
477   if (boolean_trap_result && (target_proto != proto))
478   {
479     ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Target object is non-extensible and trap "
480                                                      "returned different prototype."));
481   }
482 
483   ecma_free_value (target_proto);
484 
485   /* 17. */
486   return ret_value;
487 } /* ecma_proxy_object_set_prototype_of */
488 
489 /**
490  * The Proxy object [[isExtensible]] internal routine
491  *
492  * See also:
493  *          ECMAScript v6, 9.5.3
494  *
495  * Note: Returned value is always a simple value so freeing it is unnecessary.
496  *
497  * @return ECMA_VALUE_ERROR - if the operation fails
498  *         ECMA_VALUE_{TRUE/FALSE} - depends on whether the object is extensible
499  */
500 ecma_value_t
ecma_proxy_object_is_extensible(ecma_object_t * obj_p)501 ecma_proxy_object_is_extensible (ecma_object_t *obj_p) /**< proxy object */
502 {
503   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
504 
505   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
506 
507   /* 1. */
508   ecma_value_t handler = proxy_obj_p->handler;
509 
510   /* 2-5. */
511   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_IS_EXTENSIBLE);
512 
513   /* 6. */
514   if (ECMA_IS_VALUE_ERROR (trap))
515   {
516     return trap;
517   }
518 
519   ecma_value_t target = proxy_obj_p->target;
520   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
521 
522   /* 7. */
523   if (ecma_is_value_undefined (trap))
524   {
525     return ecma_builtin_object_object_is_extensible (target_obj_p);
526   }
527 
528   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
529 
530   /* 8. */
531   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, &target, 1);
532 
533   ecma_deref_object (func_obj_p);
534 
535   /* 9. */
536   if (ECMA_IS_VALUE_ERROR (trap_result))
537   {
538     return trap_result;
539   }
540 
541   bool boolean_trap_result = ecma_op_to_boolean (trap_result);
542 
543   ecma_free_value (trap_result);
544 
545   bool target_result;
546 
547   /* 10. */
548   if (ECMA_OBJECT_IS_PROXY (target_obj_p))
549   {
550     ecma_value_t proxy_is_ext = ecma_proxy_object_is_extensible (target_obj_p);
551 
552     if (ECMA_IS_VALUE_ERROR (proxy_is_ext))
553     {
554       return proxy_is_ext;
555     }
556 
557     target_result = ecma_is_value_true (proxy_is_ext);
558   }
559   else
560   {
561     target_result = ecma_op_ordinary_object_is_extensible (target_obj_p);
562   }
563 
564   /* 12. */
565   if (boolean_trap_result != target_result)
566   {
567     return ecma_raise_type_error (ECMA_ERR_MSG ("Trap result does not reflect extensibility of proxy target"));
568   }
569 
570   return ecma_make_boolean_value (boolean_trap_result);
571 } /* ecma_proxy_object_is_extensible */
572 
573 /**
574  * The Proxy object [[PreventExtensions]] internal routine
575  *
576  * See also:
577  *          ECMAScript v6, 9.5.4
578  *
579  * Note: Returned value is always a simple value so freeing it is unnecessary.
580  *
581  * @return ECMA_VALUE_ERROR - if the operation fails
582  *         ECMA_VALUE_{TRUE/FALSE} - depends on whether the object can be set as inextensible
583  */
584 ecma_value_t
ecma_proxy_object_prevent_extensions(ecma_object_t * obj_p)585 ecma_proxy_object_prevent_extensions (ecma_object_t *obj_p) /**< proxy object */
586 {
587   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
588 
589   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
590 
591   /* 1. */
592   ecma_value_t handler = proxy_obj_p->handler;
593 
594   /* 2-5. */
595   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_PREVENT_EXTENSIONS_UL);
596 
597   /* 6. */
598   if (ECMA_IS_VALUE_ERROR (trap))
599   {
600     return trap;
601   }
602 
603   ecma_value_t target = proxy_obj_p->target;
604   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
605 
606   /* 7. */
607   if (ecma_is_value_undefined (trap))
608   {
609     ecma_value_t ret_value = ecma_builtin_object_object_prevent_extensions (target_obj_p);
610 
611     if (ECMA_IS_VALUE_ERROR (ret_value))
612     {
613       return ret_value;
614     }
615 
616     ecma_deref_object (target_obj_p);
617 
618     return ECMA_VALUE_TRUE;
619   }
620 
621   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
622 
623   /* 8. */
624   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, &target, 1);
625 
626   ecma_deref_object (func_obj_p);
627 
628   /* 9. */
629   if (ECMA_IS_VALUE_ERROR (trap_result))
630   {
631     return trap_result;
632   }
633 
634   bool boolean_trap_result = ecma_op_to_boolean (trap_result);
635 
636   ecma_free_value (trap_result);
637 
638   /* 10. */
639   if (boolean_trap_result)
640   {
641     ecma_value_t target_is_ext = ecma_builtin_object_object_is_extensible (target_obj_p);
642 
643     if (ECMA_IS_VALUE_ERROR (target_is_ext))
644     {
645       return target_is_ext;
646     }
647 
648     if (ecma_is_value_true (target_is_ext))
649     {
650       return ecma_raise_type_error (ECMA_ERR_MSG ("Trap result does not reflect inextensibility of proxy target"));
651     }
652   }
653 
654   /* 11. */
655   return ecma_make_boolean_value (boolean_trap_result);
656 } /* ecma_proxy_object_prevent_extensions */
657 
658 /**
659  * The Proxy object [[GetOwnProperty]] internal routine
660  *
661  * See also:
662  *          ECMAScript v6, 9.5.5
663  *
664  * Note: - Returned value is always a simple value so freeing it is unnecessary.
665  *       - If the operation does not fail, freeing the filled property descriptor is the caller's responsibility
666  *
667  * @return ECMA_VALUE_ERROR - if the operation fails
668  *         ECMA_VALUE_{TRUE_FALSE} - depends on whether object has property with the given name
669  */
670 ecma_value_t
ecma_proxy_object_get_own_property_descriptor(ecma_object_t * obj_p,ecma_string_t * prop_name_p,ecma_property_descriptor_t * prop_desc_p)671 ecma_proxy_object_get_own_property_descriptor (ecma_object_t *obj_p, /**< proxy object */
672                                                ecma_string_t *prop_name_p, /**< property name */
673                                                ecma_property_descriptor_t *prop_desc_p) /**< [out] property
674                                                                                          *   descriptor */
675 {
676   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
677 
678   /* 2. */
679   ecma_value_t handler = proxy_obj_p->handler;
680 
681   /* 3-6. */
682   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL);
683 
684   ecma_value_t target = proxy_obj_p->target;
685 
686   /* 7. */
687   if (ECMA_IS_VALUE_ERROR (trap))
688   {
689     return trap;
690   }
691 
692   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
693 
694   /* 8. */
695   if (ecma_is_value_undefined (trap))
696   {
697     return ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, prop_desc_p);
698   }
699 
700   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
701   ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p);
702   ecma_value_t args[] = { target, prop_value };
703 
704   /* 9. */
705   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2);
706   ecma_deref_object (func_obj_p);
707 
708   /* 10. */
709   if (ECMA_IS_VALUE_ERROR (trap_result))
710   {
711     return trap_result;
712   }
713 
714   /* 11. */
715   if (!ecma_is_value_object (trap_result) && !ecma_is_value_undefined (trap_result))
716   {
717     ecma_free_value (trap_result);
718     return ecma_raise_type_error (ECMA_ERR_MSG ("Trap is not object nor undefined."));
719   }
720 
721   /* 12. */
722   ecma_property_descriptor_t target_desc;
723   ecma_value_t target_status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc);
724 
725   /* 13. */
726   if (ECMA_IS_VALUE_ERROR (target_status))
727   {
728     ecma_free_value (trap_result);
729     return target_status;
730   }
731 
732   /* 14. */
733   if (ecma_is_value_undefined (trap_result))
734   {
735     /* .a */
736     if (ecma_is_value_false (target_status))
737     {
738       return ECMA_VALUE_FALSE;
739     }
740     /* .b */
741     if (!(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE))
742     {
743       ecma_free_property_descriptor (&target_desc);
744       return ecma_raise_type_error (ECMA_ERR_MSG ("Given property is a non-configurable"
745                                                   " data property on the proxy target"));
746     }
747 
748     /* .c */
749     ecma_free_property_descriptor (&target_desc);
750     ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
751 
752     /* .d */
753     if (ECMA_IS_VALUE_ERROR (extensible_target))
754     {
755       return extensible_target;
756     }
757 
758     /* .e */
759     JERRY_ASSERT (ecma_is_value_boolean (extensible_target));
760 
761     /* .f */
762     if (ecma_is_value_false (extensible_target))
763     {
764       return ecma_raise_type_error (ECMA_ERR_MSG ("Target not extensible."));
765     }
766 
767     /* .g */
768     return ECMA_VALUE_FALSE;
769   }
770 
771   /* 15. */
772   ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
773 
774   /* 16. */
775   if (ECMA_IS_VALUE_ERROR (extensible_target))
776   {
777     if (ecma_is_value_true (target_status))
778     {
779       ecma_free_property_descriptor (&target_desc);
780     }
781     ecma_free_value (trap_result);
782     return extensible_target;
783   }
784 
785   /* 17, 19 */
786   ecma_value_t result_val = ecma_op_to_property_descriptor (trap_result, prop_desc_p);
787 
788   ecma_op_to_complete_property_descriptor (prop_desc_p);
789   ecma_free_value (trap_result);
790 
791   /* 18. */
792   if (ECMA_IS_VALUE_ERROR (result_val))
793   {
794     if (ecma_is_value_true (target_status))
795     {
796       ecma_free_property_descriptor (&target_desc);
797     }
798     return result_val;
799   }
800 
801   /* 20. */
802   bool is_extensible = ecma_is_value_true (extensible_target);
803 
804   bool is_valid = ecma_op_is_compatible_property_descriptor (prop_desc_p,
805                                                              (ecma_is_value_true (target_status) ? &target_desc : NULL),
806                                                              is_extensible);
807 
808   bool target_has_desc = ecma_is_value_true (target_status);
809   bool target_is_configurable = false;
810 
811   if (target_has_desc)
812   {
813     target_is_configurable = ((target_desc.flags & ECMA_PROP_IS_CONFIGURABLE) != 0);
814     ecma_free_property_descriptor (&target_desc);
815   }
816 
817   /* 21. */
818   if (!is_valid)
819   {
820     ecma_free_property_descriptor (prop_desc_p);
821     return ecma_raise_type_error (ECMA_ERR_MSG ("The two descriptors are not compatible."));
822   }
823 
824   /* 22. */
825   else if (!(prop_desc_p->flags & ECMA_PROP_IS_CONFIGURABLE))
826   {
827     if (!target_has_desc || target_is_configurable)
828     {
829       ecma_free_property_descriptor (prop_desc_p);
830       return ecma_raise_type_error (ECMA_ERR_MSG ("Not compatible."));
831     }
832   }
833   return ECMA_VALUE_TRUE;
834 } /* ecma_proxy_object_get_own_property_descriptor */
835 
836 /**
837  * The Proxy object [[DefineOwnProperty]] internal routine
838  *
839  * See also:
840  *          ECMAScript v6, 9.5.6
841  *
842  * Note: Returned value is always a simple value so freeing it is unnecessary.
843  *
844  * @return ECMA_VALUE_ERROR - if the operation fails
845  *         ECMA_VALUE_{TRUE_FALSE} - depends on whether the property can be defined for the given object
846  */
847 ecma_value_t
ecma_proxy_object_define_own_property(ecma_object_t * obj_p,ecma_string_t * prop_name_p,const ecma_property_descriptor_t * prop_desc_p)848 ecma_proxy_object_define_own_property (ecma_object_t *obj_p, /**< proxy object */
849                                        ecma_string_t *prop_name_p, /**< property name */
850                                        const ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */
851 {
852   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
853 
854   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
855 
856   /* 2. */
857   ecma_value_t handler = proxy_obj_p->handler;
858 
859   /* 3-6. */
860   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_DEFINE_PROPERTY_UL);
861 
862   /* 7. */
863   if (ECMA_IS_VALUE_ERROR (trap))
864   {
865     return trap;
866   }
867 
868   ecma_value_t target = proxy_obj_p->target;
869   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
870 
871   /* 8. */
872   if (ecma_is_value_undefined (trap))
873   {
874     return ecma_op_object_define_own_property (target_obj_p, prop_name_p, prop_desc_p);
875   }
876 
877   /* 9. */
878   ecma_object_t *desc_obj = ecma_op_from_property_descriptor (prop_desc_p);
879 
880   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
881   ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p);
882   ecma_value_t desc_obj_value = ecma_make_object_value (desc_obj);
883   ecma_value_t args[] = {target, prop_value, desc_obj_value};
884 
885   /* 10. */
886   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 3);
887 
888   ecma_deref_object (func_obj_p);
889   ecma_deref_object (desc_obj);
890 
891   /* 11. */
892   if (ECMA_IS_VALUE_ERROR (trap_result))
893   {
894     return trap_result;
895   }
896 
897   bool boolean_trap_result = ecma_op_to_boolean (trap_result);
898 
899   ecma_free_value (trap_result);
900 
901   /* 12. */
902   if (!boolean_trap_result)
903   {
904     return ECMA_VALUE_FALSE;
905   }
906 
907   /* 13. */
908   ecma_property_descriptor_t target_desc;
909 
910   ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc);
911 
912   /* 14. */
913   if (ECMA_IS_VALUE_ERROR (status))
914   {
915     return status;
916   }
917 
918   bool target_prop_found = ecma_is_value_true (status);
919 
920   /* 15. */
921   ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
922 
923   bool is_target_ext = ecma_is_value_true (extensible_target);
924 
925   /* 16. */
926   if (ECMA_IS_VALUE_ERROR (extensible_target))
927   {
928     if (target_prop_found)
929     {
930       ecma_free_property_descriptor (&target_desc);
931     }
932 
933     return extensible_target;
934   }
935 
936   /* 17. */
937   bool setting_config_false = ((prop_desc_p->flags & ECMA_PROP_IS_CONFIGURABLE_DEFINED)
938                                 && !(prop_desc_p->flags & ECMA_PROP_IS_CONFIGURABLE));
939 
940   /* 19. */
941   if (!target_prop_found)
942   {
943     if (!is_target_ext)
944     {
945       return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned truish for adding property "
946                                                   "to the non-extensible target"));
947     }
948 
949     if (setting_config_false)
950     {
951       return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned truish for defining non-configurable property "
952                                                   "which is non-existent in the target"));
953     }
954   }
955   /* 20. */
956   else
957   {
958     ecma_value_t ret_value = ECMA_VALUE_EMPTY;
959 
960     if (!ecma_op_is_compatible_property_descriptor (prop_desc_p, &target_desc, is_target_ext))
961     {
962       ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned truish for adding property that is "
963                                                        "incompatible with the existing property in the target"));
964     }
965     else if (setting_config_false && (target_desc.flags & ECMA_PROP_IS_CONFIGURABLE))
966     {
967       ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned truish for defining non-configurable property "
968                                                        "which is configurable in the target"));
969     }
970 
971     ecma_free_property_descriptor (&target_desc);
972 
973     if (ECMA_IS_VALUE_ERROR (ret_value))
974     {
975       return ret_value;
976     }
977   }
978 
979   return ECMA_VALUE_TRUE;
980 } /* ecma_proxy_object_define_own_property */
981 
982 /**
983  * The Proxy object [[HasProperty]] internal routine
984  *
985  * See also:
986  *          ECMAScript v6, 9.5.7
987  *
988  * Note: Returned value is always a simple value so freeing it is unnecessary.
989  *
990  * @return ECMA_VALUE_ERROR - if the operation fails
991  *         ECMA_VALUE_{TRUE_FALSE} - depends on whether the property is found
992  */
993 ecma_value_t
ecma_proxy_object_has(ecma_object_t * obj_p,ecma_string_t * prop_name_p)994 ecma_proxy_object_has (ecma_object_t *obj_p, /**< proxy object */
995                        ecma_string_t *prop_name_p) /**< property name */
996 {
997   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
998   ECMA_CHECK_STACK_USAGE ();
999 
1000   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
1001 
1002   /* 2. */
1003   ecma_value_t handler = proxy_obj_p->handler;
1004 
1005   /* 3-6. */
1006   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_HAS);
1007 
1008   /* 7. */
1009   if (ECMA_IS_VALUE_ERROR (trap))
1010   {
1011     return trap;
1012   }
1013 
1014   ecma_value_t target = proxy_obj_p->target;
1015   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
1016 
1017   /* 8. */
1018   if (ecma_is_value_undefined (trap))
1019   {
1020     return ecma_op_object_has_property (target_obj_p, prop_name_p);
1021   }
1022 
1023   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
1024   ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p);
1025   ecma_value_t args[] = {target, prop_value};
1026 
1027   /* 9. */
1028   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2);
1029 
1030   ecma_deref_object (func_obj_p);
1031 
1032   /* 10. */
1033   if (ECMA_IS_VALUE_ERROR (trap_result))
1034   {
1035     return trap_result;
1036   }
1037 
1038   bool boolean_trap_result = ecma_op_to_boolean (trap_result);
1039 
1040   ecma_free_value (trap_result);
1041 
1042   /* 11. */
1043   if (!boolean_trap_result)
1044   {
1045     ecma_property_descriptor_t target_desc;
1046 
1047     ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc);
1048 
1049     if (ECMA_IS_VALUE_ERROR (status))
1050     {
1051       return status;
1052     }
1053 
1054     if (ecma_is_value_true (status))
1055     {
1056       bool prop_is_configurable = target_desc.flags & ECMA_PROP_IS_CONFIGURABLE;
1057 
1058       ecma_free_property_descriptor (&target_desc);
1059 
1060       if (!prop_is_configurable)
1061       {
1062         return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned falsish for property which exists "
1063                                                     "in the proxy target as non-configurable"));
1064       }
1065 
1066       ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
1067 
1068       if (ECMA_IS_VALUE_ERROR (extensible_target))
1069       {
1070         return extensible_target;
1071       }
1072 
1073       if (ecma_is_value_false (extensible_target))
1074       {
1075         return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned falsish for property but "
1076                                                     "the proxy target is not extensible"));
1077       }
1078     }
1079   }
1080 
1081   /* 12. */
1082   return ecma_make_boolean_value (boolean_trap_result);
1083 } /* ecma_proxy_object_has */
1084 
1085 /**
1086  * The Proxy object [[Get]] internal routine
1087  *
1088  * See also:
1089  *          ECMAScript v6, 9.5.8
1090  *
1091  * Note: Returned value is always a simple value so freeing it is unnecessary.
1092  *
1093  * @return ECMA_VALUE_ERROR - if the operation fails
1094  *         value of the given nameddata property or the result of the getter function call - otherwise
1095  */
1096 ecma_value_t
ecma_proxy_object_get(ecma_object_t * obj_p,ecma_string_t * prop_name_p,ecma_value_t receiver)1097 ecma_proxy_object_get (ecma_object_t *obj_p, /**< proxy object */
1098                        ecma_string_t *prop_name_p, /**< property name */
1099                        ecma_value_t receiver) /**< receiver to invoke getter function */
1100 {
1101   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
1102   ECMA_CHECK_STACK_USAGE ();
1103 
1104   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
1105 
1106   /* 2. */
1107   ecma_value_t handler = proxy_obj_p->handler;
1108 
1109   /* 3-6. */
1110   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_GET);
1111 
1112   /* 7. */
1113   if (ECMA_IS_VALUE_ERROR (trap))
1114   {
1115     return trap;
1116   }
1117 
1118   ecma_value_t target = proxy_obj_p->target;
1119   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
1120 
1121   /* 8. */
1122   if (ecma_is_value_undefined (trap))
1123   {
1124     return ecma_op_object_get_with_receiver (target_obj_p, prop_name_p, receiver);
1125   }
1126 
1127   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
1128   ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p);
1129   ecma_value_t args[] = { target, prop_value, receiver };
1130 
1131   /* 9. */
1132   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 3);
1133 
1134   ecma_deref_object (func_obj_p);
1135 
1136   /* 10. */
1137   if (ECMA_IS_VALUE_ERROR (trap_result))
1138   {
1139     return trap_result;
1140   }
1141 
1142   /* 11. */
1143   ecma_property_descriptor_t target_desc;
1144 
1145   ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc);
1146 
1147   /* 12. */
1148   if (ECMA_IS_VALUE_ERROR (status))
1149   {
1150     return status;
1151   }
1152 
1153   /* 13. */
1154   if (ecma_is_value_true (status))
1155   {
1156     ecma_value_t ret_value = ECMA_VALUE_EMPTY;
1157 
1158     if ((target_desc.flags & ECMA_PROP_IS_VALUE_DEFINED)
1159         && !(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE)
1160         && !(target_desc.flags & ECMA_PROP_IS_WRITABLE)
1161         && !ecma_op_same_value (trap_result, target_desc.value))
1162     {
1163       ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("given property is a read-only and non-configurable"
1164                                                        " data property on the proxy target"));
1165     }
1166     else if (!(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE)
1167             && (target_desc.flags & (ECMA_PROP_IS_GET_DEFINED | ECMA_PROP_IS_SET_DEFINED))
1168             && target_desc.get_p == NULL
1169             && !ecma_is_value_undefined (trap_result))
1170     {
1171       ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("given property is a non-configurable property and"
1172                                                        " does not have a getter function"));
1173     }
1174 
1175     ecma_free_property_descriptor (&target_desc);
1176 
1177     if (ECMA_IS_VALUE_ERROR (ret_value))
1178     {
1179       ecma_free_value (trap_result);
1180 
1181       return ret_value;
1182     }
1183   }
1184 
1185   /* 14. */
1186   return trap_result;
1187 } /* ecma_proxy_object_get */
1188 
1189 /**
1190  * The Proxy object [[Set]] internal routine
1191  *
1192  * See also:
1193  *          ECMAScript v6, 9.5.9
1194  *
1195  * Note: Returned value is always a simple value so freeing it is unnecessary.
1196  *
1197  * @return ECMA_VALUE_ERROR - if the operation fails
1198  *         ECMA_VALUE_{TRUE/FALSE} - depends on whether the propety can be set to the given object
1199  */
1200 ecma_value_t
ecma_proxy_object_set(ecma_object_t * obj_p,ecma_string_t * prop_name_p,ecma_value_t value,ecma_value_t receiver)1201 ecma_proxy_object_set (ecma_object_t *obj_p, /**< proxy object */
1202                        ecma_string_t *prop_name_p, /**< property name */
1203                        ecma_value_t value, /**< value to set */
1204                        ecma_value_t receiver) /**< receiver to invoke setter function */
1205 {
1206   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
1207   ECMA_CHECK_STACK_USAGE ();
1208 
1209   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
1210 
1211   /* 2. */
1212   ecma_value_t handler = proxy_obj_p->handler;
1213 
1214   /* 3-6. */
1215   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_SET);
1216 
1217   /* 7. */
1218   if (ECMA_IS_VALUE_ERROR (trap))
1219   {
1220     return trap;
1221   }
1222 
1223   ecma_value_t target = proxy_obj_p->target;
1224   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
1225 
1226   /* 8. */
1227   if (ecma_is_value_undefined (trap))
1228   {
1229     return ecma_op_object_put_with_receiver (target_obj_p, prop_name_p, value, receiver, false);
1230   }
1231 
1232   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
1233   ecma_value_t prop_name_value = ecma_make_prop_name_value (prop_name_p);
1234   ecma_value_t args[] = { target, prop_name_value, value, receiver };
1235 
1236   /* 9. */
1237   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 4);
1238 
1239   ecma_deref_object (func_obj_p);
1240 
1241   /* 10. */
1242   if (ECMA_IS_VALUE_ERROR (trap_result))
1243   {
1244     return trap_result;
1245   }
1246 
1247   bool boolean_trap_result = ecma_op_to_boolean (trap_result);
1248 
1249   ecma_free_value (trap_result);
1250 
1251   /* 11. */
1252   if (!boolean_trap_result)
1253   {
1254     return ECMA_VALUE_FALSE;
1255   }
1256 
1257   /* 12. */
1258   ecma_property_descriptor_t target_desc;
1259 
1260   ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc);
1261 
1262   /* 13. */
1263   if (ECMA_IS_VALUE_ERROR (status))
1264   {
1265     return status;
1266   }
1267 
1268   /* 14. */
1269   if (ecma_is_value_true (status))
1270   {
1271     ecma_value_t ret_value = ECMA_VALUE_EMPTY;
1272 
1273     if ((target_desc.flags & ECMA_PROP_IS_VALUE_DEFINED)
1274         && !(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE)
1275         && !(target_desc.flags & ECMA_PROP_IS_WRITABLE)
1276         && !ecma_op_same_value (value, target_desc.value))
1277     {
1278       ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("The property exists in the proxy target as a"
1279                                                        " non-configurable and non-writable data property"
1280                                                        " with a different value."));
1281     }
1282     else if (!(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE)
1283             && (target_desc.flags & (ECMA_PROP_IS_GET_DEFINED | ECMA_PROP_IS_SET_DEFINED))
1284             && target_desc.set_p == NULL)
1285     {
1286       ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("The property exists in the proxy target as a"
1287                                                        " non-configurable accessor property whitout a setter."));
1288     }
1289 
1290     ecma_free_property_descriptor (&target_desc);
1291 
1292     if (ECMA_IS_VALUE_ERROR (ret_value))
1293     {
1294       return ret_value;
1295     }
1296   }
1297 
1298   /* 15. */
1299   return ECMA_VALUE_TRUE;
1300 } /* ecma_proxy_object_set */
1301 
1302 /**
1303  * The Proxy object [[Delete]] internal routine
1304  *
1305  * See also:
1306  *          ECMAScript v6, 9.5.10
1307  *
1308  * Note: Returned value is always a simple value so freeing it is unnecessary.
1309  *
1310  * @return ECMA_VALUE_ERROR - if the operation fails
1311  *         ECMA_VALUE_{TRUE/FALSE} - depends on whether the propety can be deleted
1312  */
1313 ecma_value_t
ecma_proxy_object_delete_property(ecma_object_t * obj_p,ecma_string_t * prop_name_p)1314 ecma_proxy_object_delete_property (ecma_object_t *obj_p, /**< proxy object */
1315                                    ecma_string_t *prop_name_p) /**< property name */
1316 {
1317   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
1318 
1319   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
1320 
1321   /* 2. */
1322   ecma_value_t handler = proxy_obj_p->handler;
1323 
1324   /* 3-6.*/
1325   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_DELETE_PROPERTY_UL);
1326 
1327   /* 7. */
1328   if (ECMA_IS_VALUE_ERROR (trap))
1329   {
1330     return trap;
1331   }
1332 
1333   ecma_value_t target = proxy_obj_p->target;
1334   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
1335 
1336   /* 8. */
1337   if (ecma_is_value_undefined (trap))
1338   {
1339     return ecma_op_object_delete (target_obj_p, prop_name_p, false);
1340   }
1341 
1342   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
1343   ecma_value_t prop_name_value = ecma_make_prop_name_value (prop_name_p);
1344   ecma_value_t args[] = { target, prop_name_value };
1345 
1346   /* 9. */
1347   ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2);
1348 
1349   ecma_deref_object (func_obj_p);
1350 
1351   /* 10. */
1352   if (ECMA_IS_VALUE_ERROR (trap_result))
1353   {
1354     return trap_result;
1355   }
1356 
1357   bool boolean_trap_result = ecma_op_to_boolean (trap_result);
1358 
1359   ecma_free_value (trap_result);
1360 
1361   /* 11. */
1362   if (!boolean_trap_result)
1363   {
1364     return ECMA_VALUE_FALSE;
1365   }
1366 
1367   /* 12. */
1368   ecma_property_descriptor_t target_desc;
1369 
1370   ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc);
1371 
1372   /* 13. */
1373   if (ECMA_IS_VALUE_ERROR (status))
1374   {
1375     return status;
1376   }
1377 
1378   /* 14. */
1379   if (ecma_is_value_false (status))
1380   {
1381     return ECMA_VALUE_TRUE;
1382   }
1383 
1384   ecma_value_t ret_value = ECMA_VALUE_TRUE;
1385 
1386   /* 15. */
1387   if (!(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE))
1388   {
1389     ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned truish for property which is "
1390                                                      "non-configurable in the proxy target."));
1391   }
1392 
1393   ecma_free_property_descriptor (&target_desc);
1394 
1395   /* 16. */
1396   return ret_value;
1397 } /* ecma_proxy_object_delete_property */
1398 
1399 /**
1400  * The Proxy object [[Enumerate]] internal routine
1401  *
1402  * See also:
1403  *          ECMAScript v6, 9.5.11
1404  *
1405  * Note: Returned value must be freed with ecma_free_value.
1406  *
1407  * @return ECMA_VALUE_ERROR - if the operation fails
1408  *         ecma-object - otherwise
1409  */
1410 ecma_value_t
ecma_proxy_object_enumerate(ecma_object_t * obj_p)1411 ecma_proxy_object_enumerate (ecma_object_t *obj_p) /**< proxy object */
1412 {
1413   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
1414   JERRY_UNUSED (obj_p);
1415   return ecma_raise_type_error (ECMA_ERR_MSG ("UNIMPLEMENTED: Proxy.[[Enumerate]]"));
1416 } /* ecma_proxy_object_enumerate */
1417 
1418 /**
1419  * Helper method for the Proxy object [[OwnPropertyKeys]] operation
1420  *
1421  * See also:
1422  *          ECMAScript v6, 9.5.12 steps 21. 23.
1423  *
1424  * @return ECMA_VALUE_ERROR - if a target key is not in the unchecked_result_keys collection
1425  *         ECMA_VALUE_EMPTY - otherwise
1426  */
1427 static ecma_value_t
ecma_proxy_object_own_property_keys_helper(ecma_collection_t * target_collection,ecma_collection_t * unchecked_result_keys,uint32_t * counter)1428 ecma_proxy_object_own_property_keys_helper (ecma_collection_t *target_collection, /**< target keys */
1429                                             ecma_collection_t *unchecked_result_keys, /**< unchecked keys */
1430                                             uint32_t *counter) /**< unchecked property counter */
1431 {
1432   ecma_value_t ret_value = ECMA_VALUE_EMPTY;
1433 
1434   for (uint32_t i = 0; i < target_collection->item_count; i++)
1435   {
1436     ecma_string_t *current_prop_name = ecma_get_prop_name_from_value (target_collection->buffer_p[i]);
1437 
1438     ret_value = ECMA_VALUE_ERROR;
1439 
1440     for (uint32_t j = 0; j < unchecked_result_keys->item_count; j++)
1441     {
1442       if (ecma_is_value_empty (unchecked_result_keys->buffer_p[j]))
1443       {
1444         continue;
1445       }
1446 
1447       ecma_string_t *unchecked_prop_name = ecma_get_prop_name_from_value (unchecked_result_keys->buffer_p[j]);
1448 
1449       if (ecma_compare_ecma_strings (current_prop_name, unchecked_prop_name))
1450       {
1451         ecma_deref_ecma_string (unchecked_prop_name);
1452         ret_value = ECMA_VALUE_EMPTY;
1453         unchecked_result_keys->buffer_p[j] = ECMA_VALUE_EMPTY;
1454         (*counter)++;
1455       }
1456     }
1457 
1458     if (ECMA_IS_VALUE_ERROR (ret_value))
1459     {
1460       break;
1461     }
1462   }
1463 
1464   return ret_value;
1465 } /* ecma_proxy_object_own_property_keys_helper */
1466 
1467 /**
1468  * Helper method for checking the invariants in the Proxy object [[OwnPropertyKeys]] operation
1469  *
1470  * See also:
1471  *          ECMAScript v6, 9.5.12 steps 20-25.
1472  *
1473  * @return true - if none of the invariants got violated
1474  *         false - otherwise
1475  */
1476 static bool
ecma_proxy_check_invariants_for_own_prop_keys(ecma_collection_t * trap_result,ecma_collection_t * target_non_configurable_keys,ecma_collection_t * target_configurable_keys,ecma_value_t extensible_target)1477 ecma_proxy_check_invariants_for_own_prop_keys (ecma_collection_t *trap_result,
1478                                                ecma_collection_t *target_non_configurable_keys,
1479                                                ecma_collection_t *target_configurable_keys,
1480                                                ecma_value_t extensible_target)
1481 {
1482   /* 20. */
1483   ecma_collection_t *unchecked_result_keys = ecma_new_collection ();
1484 
1485   ecma_collection_append (unchecked_result_keys, trap_result->buffer_p, trap_result->item_count);
1486 
1487   for (uint32_t i = 0; i < unchecked_result_keys->item_count; i++)
1488   {
1489     ecma_string_t *unchecked_prop_name = ecma_get_prop_name_from_value (unchecked_result_keys->buffer_p[i]);
1490     ecma_ref_ecma_string (unchecked_prop_name);
1491   }
1492 
1493   bool check_ok = false;
1494   uint32_t unchecked_prop_name_counter = 0;
1495 
1496   /* 21. */
1497   if (ECMA_IS_VALUE_ERROR (ecma_proxy_object_own_property_keys_helper (target_non_configurable_keys,
1498                                                                        unchecked_result_keys,
1499                                                                        &unchecked_prop_name_counter)))
1500   {
1501     ecma_raise_type_error (ECMA_ERR_MSG ("Trap result did not include all non-configurable keys."));
1502   }
1503   /* 22. */
1504   else if (ecma_is_value_true (extensible_target))
1505   {
1506     check_ok = true;
1507   }
1508   /* 23. */
1509   else if (ECMA_IS_VALUE_ERROR (ecma_proxy_object_own_property_keys_helper (target_configurable_keys,
1510                                                                             unchecked_result_keys,
1511                                                                             &unchecked_prop_name_counter)))
1512   {
1513     ecma_raise_type_error (ECMA_ERR_MSG ("Trap result did not include all configurable keys."));
1514   }
1515   /* 24. */
1516   else if (unchecked_result_keys->item_count != unchecked_prop_name_counter)
1517   {
1518     ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned extra keys but proxy target is non-extensible"));
1519   }
1520   /* 25. */
1521   else
1522   {
1523     check_ok = true;
1524   }
1525 
1526   ecma_collection_free (unchecked_result_keys);
1527 
1528   return check_ok;
1529 } /* ecma_proxy_check_invariants_for_own_prop_keys */
1530 
1531 /**
1532  * The Proxy object [[OwnPropertyKeys]] internal routine
1533  *
1534  * See also:
1535  *          ECMAScript v6, 9.5.12
1536  *
1537  * Note: If the returned collection is not NULL, it must be freed with
1538  *       ecma_collection_free if it is no longer needed
1539  *
1540  * @return NULL - if the operation fails
1541  *         pointer to a newly allocated list of property names - otherwise
1542  */
1543 ecma_collection_t *
ecma_proxy_object_own_property_keys(ecma_object_t * obj_p)1544 ecma_proxy_object_own_property_keys (ecma_object_t *obj_p) /**< proxy object */
1545 {
1546   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
1547 
1548   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
1549 
1550   /* 1. */
1551   ecma_value_t handler = proxy_obj_p->handler;
1552 
1553   /* 2-5. */
1554   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_OWN_KEYS_UL);
1555 
1556   /* 6. */
1557   if (ECMA_IS_VALUE_ERROR (trap))
1558   {
1559     return NULL;
1560   }
1561 
1562   ecma_value_t target = proxy_obj_p->target;
1563   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
1564 
1565   /* 7. */
1566   if (ecma_is_value_undefined (trap))
1567   {
1568     return ecma_op_object_get_property_names (target_obj_p, ECMA_LIST_SYMBOLS);
1569   }
1570 
1571   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
1572 
1573   /* 8. */
1574   ecma_value_t trap_result_array = ecma_op_function_call (func_obj_p, handler, &target, 1);
1575 
1576   ecma_deref_object (func_obj_p);
1577 
1578   if (ECMA_IS_VALUE_ERROR (trap_result_array))
1579   {
1580     return NULL;
1581   }
1582 
1583   /* 9. */
1584   ecma_collection_t *trap_result = ecma_op_create_list_from_array_like (trap_result_array, true);
1585 
1586   ecma_free_value (trap_result_array);
1587 
1588   /* 10. */
1589   if (trap_result == NULL)
1590   {
1591     return trap_result;
1592   }
1593 
1594   /* 11. */
1595   ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
1596 
1597   /* 12. */
1598   if (ECMA_IS_VALUE_ERROR (extensible_target))
1599   {
1600     ecma_collection_free (trap_result);
1601     return NULL;
1602   }
1603 
1604   /* 13. */
1605   ecma_collection_t *target_keys = ecma_op_object_get_property_names (target_obj_p, ECMA_LIST_SYMBOLS);
1606 
1607   /* 14. */
1608   if (target_keys == NULL)
1609   {
1610     ecma_collection_free (trap_result);
1611     return target_keys;
1612   }
1613 
1614   /* 16. */
1615   ecma_collection_t *target_configurable_keys = ecma_new_collection ();
1616 
1617   /* 17. */
1618   ecma_collection_t *target_non_configurable_keys = ecma_new_collection ();
1619 
1620   ecma_collection_t *ret_value = NULL;
1621 
1622   /* 18. */
1623   for (uint32_t i = 0; i < target_keys->item_count; i++)
1624   {
1625     ecma_property_descriptor_t target_desc;
1626 
1627     ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (target_keys->buffer_p[i]);
1628 
1629     ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p,
1630                                                                       prop_name_p,
1631                                                                       &target_desc);
1632 
1633     if (ECMA_IS_VALUE_ERROR (status))
1634     {
1635       ecma_collection_free (trap_result);
1636       goto free_target_collections;
1637     }
1638 
1639     ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p);
1640 
1641     if (ecma_is_value_true (status)
1642         && !(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE))
1643     {
1644       ecma_collection_push_back (target_non_configurable_keys, prop_value);
1645     }
1646     else
1647     {
1648       ecma_collection_push_back (target_configurable_keys, prop_value);
1649     }
1650 
1651     if (ecma_is_value_true (status))
1652     {
1653       ecma_free_property_descriptor (&target_desc);
1654     }
1655   }
1656 
1657   /* 19. */
1658   if (ecma_is_value_true (extensible_target) && target_non_configurable_keys->item_count == 0)
1659   {
1660     ret_value = trap_result;
1661   }
1662   /* 20-25. */
1663   else if (ecma_proxy_check_invariants_for_own_prop_keys (trap_result,
1664                                                           target_non_configurable_keys,
1665                                                           target_configurable_keys,
1666                                                           extensible_target))
1667   {
1668     ret_value = trap_result;
1669   }
1670   else
1671   {
1672     JERRY_ASSERT (ret_value == NULL);
1673     ecma_collection_free (trap_result);
1674   }
1675 
1676 free_target_collections:
1677   ecma_collection_destroy (target_keys);
1678   ecma_collection_free (target_configurable_keys);
1679   ecma_collection_free (target_non_configurable_keys);
1680 
1681   return ret_value;
1682 } /* ecma_proxy_object_own_property_keys */
1683 
1684 /**
1685  * The Proxy object [[Call]] internal routine
1686  *
1687  * See also:
1688  *          ECMAScript v6, 9.5.13
1689  *
1690  * Note: Returned value must be freed with ecma_free_value.
1691  *
1692  * @return ECMA_VALUE_ERROR - if the operation fails
1693  *         result of the function call - otherwise
1694  */
1695 ecma_value_t
ecma_proxy_object_call(ecma_object_t * obj_p,ecma_value_t this_argument,const ecma_value_t * args_p,ecma_length_t argc)1696 ecma_proxy_object_call (ecma_object_t *obj_p, /**< proxy object */
1697                         ecma_value_t this_argument, /**< this argument to invoke the function */
1698                         const ecma_value_t *args_p, /**< argument list */
1699                         ecma_length_t argc) /**< number of arguments */
1700 {
1701   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
1702 
1703   ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
1704 
1705   /* 1. */
1706   ecma_value_t handler = proxy_obj_p->handler;
1707 
1708   /* 2-5.*/
1709   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_APPLY);
1710 
1711   /* 6. */
1712   if (ECMA_IS_VALUE_ERROR (trap))
1713   {
1714     return trap;
1715   }
1716 
1717   ecma_value_t target = proxy_obj_p->target;
1718 
1719   /* 7. */
1720   if (ecma_is_value_undefined (trap))
1721   {
1722     ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
1723     return ecma_op_function_call (target_obj_p, this_argument, args_p, argc);
1724   }
1725 
1726   /* 8. */
1727   ecma_value_t args_array = ecma_op_create_array_object (args_p, argc, false);
1728   ecma_value_t value_array[] = {target, this_argument, args_array};
1729   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
1730   /* 9. */
1731   ecma_value_t ret_value = ecma_op_function_call (func_obj_p, handler, value_array, 3);
1732   ecma_deref_object (func_obj_p);
1733   ecma_deref_object (ecma_get_object_from_value (args_array));
1734 
1735   return ret_value;
1736 } /* ecma_proxy_object_call */
1737 
1738 /**
1739  * The Proxy object [[Construct]] internal routine
1740  *
1741  * See also:
1742  *          ECMAScript v6, 9.5.14
1743  *
1744  * Note: Returned value must be freed with ecma_free_value.
1745  *
1746  * @return ECMA_VALUE_ERROR - if the operation fails
1747  *         result of the construct call - otherwise
1748  */
1749 ecma_value_t
ecma_proxy_object_construct(ecma_object_t * obj_p,ecma_object_t * new_target_p,const ecma_value_t * args_p,ecma_length_t argc)1750 ecma_proxy_object_construct (ecma_object_t *obj_p, /**< proxy object */
1751                              ecma_object_t *new_target_p, /**< new target */
1752                              const ecma_value_t *args_p, /**< argument list */
1753                              ecma_length_t argc) /**< number of arguments */
1754 {
1755   JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
1756 
1757   ecma_proxy_object_t * proxy_obj_p = (ecma_proxy_object_t *) obj_p;
1758 
1759   /* 1. */
1760   ecma_value_t handler = proxy_obj_p->handler;
1761 
1762   /* 2-5. */
1763   ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_CONSTRUCT);
1764 
1765   /* 6. */
1766   if (ECMA_IS_VALUE_ERROR (trap))
1767   {
1768     return trap;
1769   }
1770 
1771   ecma_value_t target = proxy_obj_p->target;
1772   ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
1773 
1774   /* 7. */
1775   if (ecma_is_value_undefined (trap))
1776   {
1777     JERRY_ASSERT (ecma_object_is_constructor (target_obj_p));
1778 
1779     return ecma_op_function_construct (target_obj_p, new_target_p, args_p, argc);
1780   }
1781 
1782   /* 8. */
1783   ecma_value_t arg_array = ecma_op_create_array_object (args_p, argc, false);
1784 
1785   ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
1786   ecma_value_t new_target_value = ecma_make_object_value (new_target_p);
1787   ecma_value_t function_call_args[] = {target, arg_array, new_target_value};
1788 
1789   /* 9. */
1790   ecma_value_t new_obj = ecma_op_function_call (func_obj_p, handler, function_call_args, 3);
1791 
1792   ecma_free_value (arg_array);
1793   ecma_deref_object (func_obj_p);
1794 
1795   /* 10 .*/
1796   if (ECMA_IS_VALUE_ERROR (new_obj))
1797   {
1798     return new_obj;
1799   }
1800 
1801   /* 11. */
1802   if (!ecma_is_value_object (new_obj))
1803   {
1804     ecma_free_value (new_obj);
1805 
1806     return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned non-object"));
1807   }
1808 
1809   /* 12. */
1810   return new_obj;
1811 } /* ecma_proxy_object_construct */
1812 
1813 #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
1814 
1815 /**
1816  * @}
1817  * @}
1818  */
1819