Skip to main content

boa_engine/builtins/proxy/
mod.rs

1//! Boa's implementation of ECMAScript's global `Proxy` object.
2//!
3//! The `Proxy` object enables you to create a proxy for another object,
4//! which can intercept and redefine fundamental operations for that object.
5//!
6//! More information:
7//!  - [ECMAScript reference][spec]
8//!  - [MDN documentation][mdn]
9//!
10//! [spec]: https://tc39.es/ecma262/#sec-proxy-objects
11//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
12
13use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject, OrdinaryObject};
14use crate::JsExpect;
15use crate::object::internal_methods::InternalMethodCallContext;
16use crate::value::JsVariant;
17use crate::{
18    Context, JsArgs, JsResult, JsString, JsValue,
19    builtins::{BuiltInObject, array},
20    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
21    error::JsNativeError,
22    js_string,
23    native_function::NativeFunction,
24    object::{
25        JsData, JsFunction, JsObject, JsPrototype,
26        internal_methods::{
27            CallValue, InternalMethodPropertyContext, InternalObjectMethods,
28            ORDINARY_INTERNAL_METHODS, is_compatible_property_descriptor,
29        },
30        shape::slot::SlotAttributes,
31    },
32    property::{PropertyDescriptor, PropertyKey},
33    realm::Realm,
34    string::StaticJsStrings,
35    value::Type,
36};
37use boa_gc::{Finalize, GcRefCell, Trace};
38use rustc_hash::FxHashSet;
39/// Javascript `Proxy` object.
40#[derive(Debug, Clone, Trace, Finalize)]
41pub struct Proxy {
42    // (target, handler)
43    data: Option<(JsObject, JsObject)>,
44}
45
46impl JsData for Proxy {
47    fn internal_methods(&self) -> &'static InternalObjectMethods {
48        static BASIC: InternalObjectMethods = InternalObjectMethods {
49            __get_prototype_of__: proxy_exotic_get_prototype_of,
50            __set_prototype_of__: proxy_exotic_set_prototype_of,
51            __is_extensible__: proxy_exotic_is_extensible,
52            __prevent_extensions__: proxy_exotic_prevent_extensions,
53            __get_own_property__: proxy_exotic_get_own_property,
54            __define_own_property__: proxy_exotic_define_own_property,
55            __has_property__: proxy_exotic_has_property,
56            __try_get__: proxy_exotic_try_get,
57            __get__: proxy_exotic_get,
58            __set__: proxy_exotic_set,
59            __delete__: proxy_exotic_delete,
60            __own_property_keys__: proxy_exotic_own_property_keys,
61            ..ORDINARY_INTERNAL_METHODS
62        };
63
64        static CALLABLE: InternalObjectMethods = InternalObjectMethods {
65            __call__: proxy_exotic_call,
66            ..BASIC
67        };
68
69        static CONSTRUCTOR: InternalObjectMethods = InternalObjectMethods {
70            __call__: proxy_exotic_call,
71            __construct__: proxy_exotic_construct,
72            ..BASIC
73        };
74
75        let Some(data) = &self.data else {
76            return &BASIC;
77        };
78
79        if data.0.is_constructor() {
80            &CONSTRUCTOR
81        } else if data.0.is_callable() {
82            &CALLABLE
83        } else {
84            &BASIC
85        }
86    }
87}
88
89impl IntrinsicObject for Proxy {
90    fn init(realm: &Realm) {
91        BuiltInBuilder::from_standard_constructor::<Self>(realm)
92            .static_method(Self::revocable, js_string!("revocable"), 2)
93            .build_without_prototype();
94    }
95
96    fn get(intrinsics: &Intrinsics) -> JsObject {
97        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
98    }
99}
100
101impl BuiltInObject for Proxy {
102    const NAME: JsString = StaticJsStrings::PROXY;
103}
104
105impl BuiltInConstructor for Proxy {
106    const CONSTRUCTOR_ARGUMENTS: usize = 2;
107    const PROTOTYPE_STORAGE_SLOTS: usize = 0;
108    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;
109
110    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
111        StandardConstructors::proxy;
112
113    /// `28.2.1.1 Proxy ( target, handler )`
114    ///
115    /// More information:
116    ///  - [ECMAScript reference][spec]
117    ///
118    /// [spec]: https://tc39.es/ecma262/#sec-proxy-target-handler
119    fn constructor(
120        new_target: &JsValue,
121        args: &[JsValue],
122        context: &mut Context,
123    ) -> JsResult<JsValue> {
124        // 1. If NewTarget is undefined, throw a TypeError exception.
125        if new_target.is_undefined() {
126            return Err(JsNativeError::typ()
127                .with_message("Proxy constructor called on undefined new target")
128                .into());
129        }
130
131        // 2. Return ? ProxyCreate(target, handler).
132        Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context).map(JsValue::from)
133    }
134}
135
136impl Proxy {
137    pub(crate) fn new(target: JsObject, handler: JsObject) -> Self {
138        Self {
139            data: Some((target, handler)),
140        }
141    }
142
143    /// This is an internal method only built for usage in the proxy internal methods.
144    ///
145    /// It returns the (target, handler) of the proxy.
146    pub(crate) fn try_data(&self) -> JsResult<(JsObject, JsObject)> {
147        self.data.clone().ok_or_else(|| {
148            JsNativeError::typ()
149                .with_message("Proxy object has empty handler and target")
150                .into()
151        })
152    }
153
154    // `10.5.14 ProxyCreate ( target, handler )`
155    //
156    // More information:
157    //  - [ECMAScript reference][spec]
158    //
159    // [spec]: https://tc39.es/ecma262/#sec-proxycreate
160    pub(crate) fn create(
161        target: &JsValue,
162        handler: &JsValue,
163        context: &mut Context,
164    ) -> JsResult<JsObject> {
165        // 1. If Type(target) is not Object, throw a TypeError exception.
166        let target = target.as_object().ok_or_else(|| {
167            JsNativeError::typ().with_message("Proxy constructor called with non-object target")
168        })?;
169
170        // 2. If Type(handler) is not Object, throw a TypeError exception.
171        let handler = handler.as_object().ok_or_else(|| {
172            JsNativeError::typ().with_message("Proxy constructor called with non-object handler")
173        })?;
174
175        // 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
176        // 4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 10.5.
177        // 5. If IsCallable(target) is true, then
178        // a. Set P.[[Call]] as specified in 10.5.12.
179        // b. If IsConstructor(target) is true, then
180        // i. Set P.[[Construct]] as specified in 10.5.13.
181        // 6. Set P.[[ProxyTarget]] to target.
182        // 7. Set P.[[ProxyHandler]] to handler.
183        let p = JsObject::from_proto_and_data_with_shared_shape(
184            context.root_shape(),
185            context.intrinsics().constructors().object().prototype(),
186            Self::new(target.clone(), handler.clone()),
187        )
188        .upcast();
189
190        // 8. Return P.
191        Ok(p)
192    }
193
194    pub(crate) fn revoker(proxy: JsObject, context: &mut Context) -> JsFunction {
195        // 3. Let revoker be ! CreateBuiltinFunction(revokerClosure, 0, "", « [[RevocableProxy]] »).
196        // 4. Set revoker.[[RevocableProxy]] to p.
197
198        NativeFunction::from_copy_closure_with_captures(
199            |_, _, revocable_proxy, _| {
200                // a. Let F be the active function object.
201                // b. Let p be F.[[RevocableProxy]].
202                // d. Set F.[[RevocableProxy]] to null.
203                if let Some(p) = std::mem::take(&mut *revocable_proxy.borrow_mut()) {
204                    // e. Assert: p is a Proxy object.
205                    // f. Set p.[[ProxyTarget]] to null.
206                    // g. Set p.[[ProxyHandler]] to null.
207                    p.downcast_mut::<Proxy>()
208                        .js_expect("[[RevocableProxy]] must be a proxy object")?
209                        .data = None;
210                }
211
212                // c. If p is null, return undefined.
213                // h. Return undefined.
214                Ok(JsValue::undefined())
215            },
216            GcRefCell::new(Some(proxy)),
217        )
218        .to_js_function(context.realm())
219    }
220
221    /// `28.2.2.1 Proxy.revocable ( target, handler )`
222    ///
223    /// More information:
224    ///  - [ECMAScript reference][spec]
225    ///
226    /// [spec]: https://tc39.es/ecma262/#sec-proxy.revocable
227    fn revocable(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
228        // 1. Let p be ? ProxyCreate(target, handler).
229        let p = Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context)?;
230
231        // Revoker creation steps on `Proxy::revoker`
232        let revoker = Self::revoker(p.clone(), context);
233
234        // 5. Let result be ! OrdinaryObjectCreate(%Object.prototype%).
235        let result = JsObject::with_object_proto(context.intrinsics());
236
237        // 6. Perform ! CreateDataPropertyOrThrow(result, "proxy", p).
238        result
239            .create_data_property_or_throw(js_string!("proxy"), p, context)
240            .js_expect("CreateDataPropertyOrThrow cannot fail here")?;
241
242        // 7. Perform ! CreateDataPropertyOrThrow(result, "revoke", revoker).
243        result
244            .create_data_property_or_throw(js_string!("revoke"), revoker, context)
245            .js_expect("CreateDataPropertyOrThrow cannot fail here")?;
246
247        // 8. Return result.
248        Ok(result.into())
249    }
250}
251
252/// `10.5.1 [[GetPrototypeOf]] ( )`
253///
254/// More information:
255///  - [ECMAScript reference][spec]
256///
257/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof
258pub(crate) fn proxy_exotic_get_prototype_of(
259    obj: &JsObject,
260    context: &mut Context,
261) -> JsResult<JsPrototype> {
262    // 1. Let handler be O.[[ProxyHandler]].
263    // 2. If handler is null, throw a TypeError exception.
264    // 3. Assert: Type(handler) is Object.
265    // 4. Let target be O.[[ProxyTarget]].
266    let (target, handler) = obj
267        .downcast_ref::<Proxy>()
268        .js_expect("Proxy object internal internal method called on non-proxy object")?
269        .try_data()?;
270
271    // 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
272    let Some(trap) = handler.get_method(js_string!("getPrototypeOf"), context)? else {
273        // 6. If trap is undefined, then
274        // a. Return ? target.[[GetPrototypeOf]]().
275        return target.__get_prototype_of__(context);
276    };
277
278    // 7. Let handlerProto be ? Call(trap, handler, « target »).
279    let handler_proto = trap.call(&handler.into(), &[target.clone().into()], context)?;
280
281    // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception.
282    let handler_proto = match handler_proto.variant() {
283        JsVariant::Object(obj) => Some(obj.clone()),
284        JsVariant::Null => None,
285        _ => {
286            return Err(JsNativeError::typ()
287                .with_message("Proxy trap result is neither object nor null")
288                .into());
289        }
290    };
291
292    // 9. Let extensibleTarget be ? IsExtensible(target).
293    // 10. If extensibleTarget is true, return handlerProto.
294    if target.is_extensible(context)? {
295        return Ok(handler_proto);
296    }
297
298    // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
299    let target_proto = target.__get_prototype_of__(context)?;
300
301    // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.
302    if handler_proto != target_proto {
303        return Err(JsNativeError::typ()
304            .with_message("Proxy trap returned unexpected prototype")
305            .into());
306    }
307
308    // 13. Return handlerProto.
309    Ok(handler_proto)
310}
311
312/// `10.5.2 [[SetPrototypeOf]] ( V )`
313///
314/// More information:
315///  - [ECMAScript reference][spec]
316///
317/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
318pub(crate) fn proxy_exotic_set_prototype_of(
319    obj: &JsObject,
320    val: JsPrototype,
321    context: &mut Context,
322) -> JsResult<bool> {
323    // 1. Let handler be O.[[ProxyHandler]].
324    // 2. If handler is null, throw a TypeError exception.
325    // 3. Assert: Type(handler) is Object.
326    // 4. Let target be O.[[ProxyTarget]].
327    let (target, handler) = obj
328        .downcast_ref::<Proxy>()
329        .js_expect("Proxy object internal internal method called on non-proxy object")?
330        .try_data()?;
331
332    // 5. Let trap be ? GetMethod(handler, "setPrototypeOf").
333    let Some(trap) = handler.get_method(js_string!("setPrototypeOf"), context)? else {
334        // 6. If trap is undefined, then
335        // a. Return ? target.[[SetPrototypeOf]](V).
336        return target.__set_prototype_of__(val, context);
337    };
338
339    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, V »)).
340    // 8. If booleanTrapResult is false, return false.
341    if !trap
342        .call(
343            &handler.into(),
344            &[
345                target.clone().into(),
346                val.clone().map_or(JsValue::null(), Into::into),
347            ],
348            context,
349        )?
350        .to_boolean()
351    {
352        return Ok(false);
353    }
354
355    // 9. Let extensibleTarget be ? IsExtensible(target).
356    // 10. If extensibleTarget is true, return true.
357    if target.is_extensible(context)? {
358        return Ok(true);
359    }
360
361    // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
362    let target_proto = target.__get_prototype_of__(context)?;
363
364    // 12. If SameValue(V, targetProto) is false, throw a TypeError exception.
365    if val != target_proto {
366        return Err(JsNativeError::typ()
367            .with_message("Proxy trap failed to set prototype")
368            .into());
369    }
370
371    // 13. Return true.
372    Ok(true)
373}
374
375/// `10.5.3 [[IsExtensible]] ( )`
376///
377/// More information:
378///  - [ECMAScript reference][spec]
379///
380/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
381pub(crate) fn proxy_exotic_is_extensible(obj: &JsObject, context: &mut Context) -> JsResult<bool> {
382    // 1. Let handler be O.[[ProxyHandler]].
383    // 2. If handler is null, throw a TypeError exception.
384    // 3. Assert: Type(handler) is Object.
385    // 4. Let target be O.[[ProxyTarget]].
386    let (target, handler) = obj
387        .downcast_ref::<Proxy>()
388        .js_expect("Proxy object internal internal method called on non-proxy object")?
389        .try_data()?;
390
391    // 5. Let trap be ? GetMethod(handler, "isExtensible").
392    let Some(trap) = handler.get_method(js_string!("isExtensible"), context)? else {
393        // 6. If trap is undefined, then
394        // a. Return ? IsExtensible(target).
395        return target.is_extensible(context);
396    };
397
398    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)).
399    let boolean_trap_result = trap
400        .call(&handler.into(), &[target.clone().into()], context)?
401        .to_boolean();
402
403    // 8. Let targetResult be ? IsExtensible(target).
404    let target_result = target.is_extensible(context)?;
405
406    // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.
407    if boolean_trap_result != target_result {
408        return Err(JsNativeError::typ()
409            .with_message("Proxy trap returned unexpected extensible value")
410            .into());
411    }
412
413    // 10. Return booleanTrapResult.
414    Ok(boolean_trap_result)
415}
416
417/// `10.5.4 [[PreventExtensions]] ( )`
418///
419/// More information:
420///  - [ECMAScript reference][spec]
421///
422/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions
423pub(crate) fn proxy_exotic_prevent_extensions(
424    obj: &JsObject,
425    context: &mut Context,
426) -> JsResult<bool> {
427    // 1. Let handler be O.[[ProxyHandler]].
428    // 2. If handler is null, throw a TypeError exception.
429    // 3. Assert: Type(handler) is Object.
430    // 4. Let target be O.[[ProxyTarget]].
431    let (target, handler) = obj
432        .downcast_ref::<Proxy>()
433        .js_expect("Proxy object internal internal method called on non-proxy object")?
434        .try_data()?;
435
436    // 5. Let trap be ? GetMethod(handler, "preventExtensions").
437    let Some(trap) = handler.get_method(js_string!("preventExtensions"), context)? else {
438        // 6. If trap is undefined, then
439        // a. Return ? target.[[PreventExtensions]]().
440        return target.__prevent_extensions__(context);
441    };
442
443    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)).
444    let boolean_trap_result = trap
445        .call(&handler.into(), &[target.clone().into()], context)?
446        .to_boolean();
447
448    // 8. If booleanTrapResult is true, then
449    if boolean_trap_result && target.is_extensible(context)? {
450        // a. Let extensibleTarget be ? IsExtensible(target).
451        // b. If extensibleTarget is true, throw a TypeError exception.
452        return Err(JsNativeError::typ()
453            .with_message("Proxy trap failed to set extensible")
454            .into());
455    }
456
457    // 9. Return booleanTrapResult.
458    Ok(boolean_trap_result)
459}
460
461/// `10.5.5 [[GetOwnProperty]] ( P )`
462///
463/// More information:
464///  - [ECMAScript reference][spec]
465///
466/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p
467pub(crate) fn proxy_exotic_get_own_property(
468    obj: &JsObject,
469    key: &PropertyKey,
470    context: &mut InternalMethodPropertyContext<'_>,
471) -> JsResult<Option<PropertyDescriptor>> {
472    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;
473
474    // 1. Let handler be O.[[ProxyHandler]].
475    // 2. If handler is null, throw a TypeError exception.
476    // 3. Assert: Type(handler) is Object.
477    // 4. Let target be O.[[ProxyTarget]].
478    let (target, handler) = obj
479        .downcast_ref::<Proxy>()
480        .js_expect("Proxy object internal internal method called on non-proxy object")?
481        .try_data()?;
482
483    // 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
484    let Some(trap) = handler.get_method(js_string!("getOwnPropertyDescriptor"), context)? else {
485        // 6. If trap is undefined, then
486        // a. Return ? target.[[GetOwnProperty]](P).
487        return target.__get_own_property__(key, context);
488    };
489
490    // 7. Let trapResultObj be ? Call(trap, handler, « target, P »).
491    let trap_result_obj = trap.call(
492        &handler.into(),
493        &[target.clone().into(), key.clone().into()],
494        context,
495    )?;
496
497    // 8. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
498    if !trap_result_obj.is_object() && !trap_result_obj.is_undefined() {
499        return Err(JsNativeError::typ()
500            .with_message("Proxy trap result is neither object nor undefined")
501            .into());
502    }
503
504    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
505    let target_desc = target.__get_own_property__(key, context)?;
506
507    // 10. If trapResultObj is undefined, then
508    if trap_result_obj.is_undefined() {
509        if let Some(desc) = target_desc {
510            // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
511            if !desc.expect_configurable() {
512                return Err(JsNativeError::typ()
513                    .with_message(
514                        "Proxy trap result is undefined and target result is not configurable",
515                    )
516                    .into());
517            }
518
519            // c. Let extensibleTarget be ? IsExtensible(target).
520            // d. If extensibleTarget is false, throw a TypeError exception.
521            if !target.is_extensible(context)? {
522                return Err(JsNativeError::typ()
523                    .with_message("Proxy trap result is undefined and target is not extensible")
524                    .into());
525            }
526            // e. Return undefined.
527            return Ok(None);
528        }
529
530        // a. If targetDesc is undefined, return undefined.
531        return Ok(None);
532    }
533
534    // 11. Let extensibleTarget be ? IsExtensible(target).
535    let extensible_target = target.is_extensible(context)?;
536
537    // 12. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
538    let result_desc = trap_result_obj.to_property_descriptor(context)?;
539
540    // 13. Call CompletePropertyDescriptor(resultDesc).
541    let result_desc = result_desc.complete_property_descriptor();
542
543    // 14. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
544    // 15. If valid is false, throw a TypeError exception.
545    if !is_compatible_property_descriptor(
546        extensible_target,
547        result_desc.clone(),
548        target_desc.clone(),
549    ) {
550        return Err(JsNativeError::typ()
551            .with_message("Proxy trap returned unexpected property")
552            .into());
553    }
554
555    // 16. If resultDesc.[[Configurable]] is false, then
556    if !result_desc.expect_configurable() {
557        // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
558        match &target_desc {
559            Some(desc) if !desc.expect_configurable() => {
560                // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
561                if result_desc.writable() == Some(false) {
562                    // i. If targetDesc.[[Writable]] is true, throw a TypeError exception.
563                    if desc.expect_writable() {
564                        return
565                            Err(JsNativeError::typ().with_message("Proxy trap result is writable and not configurable while target result is not configurable").into())
566                        ;
567                    }
568                }
569            }
570            // i. Throw a TypeError exception.
571            _ => {
572                return Err(JsNativeError::typ()
573                    .with_message(
574                        "Proxy trap result is not configurable and target result is undefined",
575                    )
576                    .into());
577            }
578        }
579    }
580
581    // 17. Return resultDesc.
582    Ok(Some(result_desc))
583}
584
585/// `10.5.6 [[DefineOwnProperty]] ( P, Desc )`
586///
587/// More information:
588///  - [ECMAScript reference][spec]
589///
590/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc
591pub(crate) fn proxy_exotic_define_own_property(
592    obj: &JsObject,
593    key: &PropertyKey,
594    desc: PropertyDescriptor,
595    context: &mut InternalMethodPropertyContext<'_>,
596) -> JsResult<bool> {
597    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;
598
599    // 1. Let handler be O.[[ProxyHandler]].
600    // 2. If handler is null, throw a TypeError exception.
601    // 3. Assert: Type(handler) is Object.
602    // 4. Let target be O.[[ProxyTarget]].
603    let (target, handler) = obj
604        .downcast_ref::<Proxy>()
605        .js_expect("Proxy object internal internal method called on non-proxy object")?
606        .try_data()?;
607
608    // 5. Let trap be ? GetMethod(handler, "defineProperty").
609    let Some(trap) = handler.get_method(js_string!("defineProperty"), context)? else {
610        // 6. If trap is undefined, then
611        // a. Return ? target.[[DefineOwnProperty]](P, Desc).
612        return target.__define_own_property__(key, desc, context);
613    };
614
615    // 7. Let descObj be FromPropertyDescriptor(Desc).
616    let desc_obj = OrdinaryObject::from_property_descriptor(Some(desc.clone()), context)?;
617
618    // 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)).
619    // 9. If booleanTrapResult is false, return false.
620    if !trap
621        .call(
622            &handler.into(),
623            &[target.clone().into(), key.clone().into(), desc_obj],
624            context,
625        )?
626        .to_boolean()
627    {
628        return Ok(false);
629    }
630
631    // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
632    let target_desc = target.__get_own_property__(key, context)?;
633
634    // 11. Let extensibleTarget be ? IsExtensible(target).
635    let extensible_target = target.is_extensible(context)?;
636
637    // 12. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then
638    let setting_config_false = matches!(desc.configurable(), Some(false));
639
640    match target_desc {
641        // 14. If targetDesc is undefined, then
642        None => {
643            // a. If extensibleTarget is false, throw a TypeError exception.
644            if !extensible_target {
645                return Err(JsNativeError::typ()
646                    .with_message("Proxy trap failed to set property")
647                    .into());
648            }
649
650            // b. If settingConfigFalse is true, throw a TypeError exception.
651            if setting_config_false {
652                return Err(JsNativeError::typ()
653                    .with_message("Proxy trap failed to set property")
654                    .into());
655            }
656        }
657        // 15. Else,
658        Some(target_desc) => {
659            // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception.
660            if !is_compatible_property_descriptor(
661                extensible_target,
662                desc.clone(),
663                Some(target_desc.clone()),
664            ) {
665                return Err(JsNativeError::typ()
666                    .with_message("Proxy trap set property to unexpected value")
667                    .into());
668            }
669
670            // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
671            if setting_config_false && target_desc.expect_configurable() {
672                return Err(JsNativeError::typ()
673                    .with_message("Proxy trap set property with unexpected configurable field")
674                    .into());
675            }
676
677            // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then
678            if target_desc.is_data_descriptor()
679                && !target_desc.expect_configurable()
680                && target_desc.expect_writable()
681            {
682                // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
683                if let Some(writable) = desc.writable()
684                    && !writable
685                {
686                    return Err(JsNativeError::typ()
687                        .with_message("Proxy trap set property with unexpected writable field")
688                        .into());
689                }
690            }
691        }
692    }
693
694    // 16. Return true.
695    Ok(true)
696}
697
698/// `10.5.7 [[HasProperty]] ( P )`
699///
700/// More information:
701///  - [ECMAScript reference][spec]
702///
703/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
704pub(crate) fn proxy_exotic_has_property(
705    obj: &JsObject,
706    key: &PropertyKey,
707    context: &mut InternalMethodPropertyContext<'_>,
708) -> JsResult<bool> {
709    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;
710
711    // 1. Let handler be O.[[ProxyHandler]].
712    // 2. If handler is null, throw a TypeError exception.
713    // 3. Assert: Type(handler) is Object.
714    // 4. Let target be O.[[ProxyTarget]].
715    let (target, handler) = obj
716        .downcast_ref::<Proxy>()
717        .js_expect("Proxy object internal internal method called on non-proxy object")?
718        .try_data()?;
719
720    // 5. Let trap be ? GetMethod(handler, "has").
721    let Some(trap) = handler.get_method(js_string!("has"), context)? else {
722        // 6. If trap is undefined, then
723        // a. Return ? target.[[HasProperty]](P).
724        return target.has_property(key.clone(), context);
725    };
726
727    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).
728    let boolean_trap_result = trap
729        .call(
730            &handler.into(),
731            &[target.clone().into(), key.clone().into()],
732            context,
733        )?
734        .to_boolean();
735
736    // 8. If booleanTrapResult is false, then
737    if !boolean_trap_result {
738        // a. Let targetDesc be ? target.[[GetOwnProperty]](P).
739        let target_desc = target.__get_own_property__(key, context)?;
740
741        // b. If targetDesc is not undefined, then
742        if let Some(target_desc) = target_desc {
743            // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
744            if !target_desc.expect_configurable() {
745                return Err(JsNativeError::typ()
746                    .with_message("Proxy trap returned unexpected property")
747                    .into());
748            }
749
750            // ii. Let extensibleTarget be ? IsExtensible(target).
751            // iii. If extensibleTarget is false, throw a TypeError exception.
752            if !target.is_extensible(context)? {
753                return Err(JsNativeError::typ()
754                    .with_message("Proxy trap returned unexpected property")
755                    .into());
756            }
757        }
758    }
759
760    // 9. Return booleanTrapResult.
761    Ok(boolean_trap_result)
762}
763
764/// Internal optimization method for `Proxy` exotic objects.
765///
766/// This method combines the internal methods `[[HasProperty]]` and `[[Get]]`.
767///
768/// More information:
769///  - [ECMAScript reference HasProperty][spec0]
770///  - [ECMAScript reference Get][spec1]
771///
772/// [spec0]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
773/// [spec1]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
774pub(crate) fn proxy_exotic_try_get(
775    obj: &JsObject,
776    key: &PropertyKey,
777    receiver: JsValue,
778    context: &mut InternalMethodPropertyContext<'_>,
779) -> JsResult<Option<JsValue>> {
780    // Note: For now, this just calls the normal methods. Could be optimized further.
781    if proxy_exotic_has_property(obj, key, context)? {
782        Ok(Some(proxy_exotic_get(obj, key, receiver, context)?))
783    } else {
784        Ok(None)
785    }
786}
787
788/// `10.5.8 [[Get]] ( P, Receiver )`
789///
790/// More information:
791///  - [ECMAScript reference][spec]
792///
793/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
794pub(crate) fn proxy_exotic_get(
795    obj: &JsObject,
796    key: &PropertyKey,
797    receiver: JsValue,
798    context: &mut InternalMethodPropertyContext<'_>,
799) -> JsResult<JsValue> {
800    // Proxy object can't be cached.
801    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;
802
803    // 1. Let handler be O.[[ProxyHandler]].
804    // 2. If handler is null, throw a TypeError exception.
805    // 3. Assert: Type(handler) is Object.
806    // 4. Let target be O.[[ProxyTarget]].
807    let (target, handler) = obj
808        .downcast_ref::<Proxy>()
809        .js_expect("Proxy object internal internal method called on non-proxy object")?
810        .try_data()?;
811
812    // 5. Let trap be ? GetMethod(handler, "get").
813    let Some(trap) = handler.get_method(js_string!("get"), context)? else {
814        // 6. If trap is undefined, then
815        // a. Return ? target.[[Get]](P, Receiver).
816        return target.__get__(key, receiver, context);
817    };
818
819    // 7. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
820    let trap_result = trap.call(
821        &handler.into(),
822        &[target.clone().into(), key.clone().into(), receiver],
823        context,
824    )?;
825
826    // 8. Let targetDesc be ? target.[[GetOwnProperty]](P).
827    let target_desc = target.__get_own_property__(key, context)?;
828
829    // 9. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
830    if let Some(target_desc) = target_desc
831        && !target_desc.expect_configurable()
832    {
833        // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
834        if target_desc.is_data_descriptor() && !target_desc.expect_writable() {
835            // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
836            if !JsValue::same_value(&trap_result, target_desc.expect_value()) {
837                return Err(JsNativeError::typ()
838                    .with_message("Proxy trap returned unexpected data descriptor")
839                    .into());
840            }
841        }
842
843        // b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then
844        if target_desc.is_accessor_descriptor() && target_desc.expect_get().is_undefined() {
845            // i. If trapResult is not undefined, throw a TypeError exception.
846            if !trap_result.is_undefined() {
847                return Err(JsNativeError::typ()
848                    .with_message("Proxy trap returned unexpected accessor descriptor")
849                    .into());
850            }
851        }
852    }
853
854    // 10. Return trapResult.
855    Ok(trap_result)
856}
857
858/// `10.5.9 [[Set]] ( P, V, Receiver )`
859///
860/// More information:
861///  - [ECMAScript reference][spec]
862///
863/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
864pub(crate) fn proxy_exotic_set(
865    obj: &JsObject,
866    key: PropertyKey,
867    value: JsValue,
868    receiver: JsValue,
869    context: &mut InternalMethodPropertyContext<'_>,
870) -> JsResult<bool> {
871    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;
872
873    // 1. Let handler be O.[[ProxyHandler]].
874    // 2. If handler is null, throw a TypeError exception.
875    // 3. Assert: Type(handler) is Object.
876    // 4. Let target be O.[[ProxyTarget]].
877    let (target, handler) = obj
878        .downcast_ref::<Proxy>()
879        .js_expect("Proxy object internal internal method called on non-proxy object")?
880        .try_data()?;
881
882    // 5. Let trap be ? GetMethod(handler, "set").
883    let Some(trap) = handler.get_method(js_string!("set"), context)? else {
884        // 6. If trap is undefined, then
885        // a. Return ? target.[[Set]](P, V, Receiver).
886        return target.__set__(key, value, receiver, context);
887    };
888
889    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).
890    // 8. If booleanTrapResult is false, return false.
891    if !trap
892        .call(
893            &handler.into(),
894            &[
895                target.clone().into(),
896                key.clone().into(),
897                value.clone(),
898                receiver,
899            ],
900            context,
901        )?
902        .to_boolean()
903    {
904        return Ok(false);
905    }
906
907    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
908    let target_desc = target.__get_own_property__(&key, context)?;
909
910    // 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
911    if let Some(target_desc) = target_desc
912        && !target_desc.expect_configurable()
913    {
914        // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
915        if target_desc.is_data_descriptor() && !target_desc.expect_writable() {
916            // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
917            if !JsValue::same_value(&value, target_desc.expect_value()) {
918                return Err(JsNativeError::typ()
919                    .with_message("Proxy trap set unexpected data descriptor")
920                    .into());
921            }
922        }
923
924        // b. If IsAccessorDescriptor(targetDesc) is true, then
925        if target_desc.is_accessor_descriptor() {
926            // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
927            match target_desc.set().map(JsValue::is_undefined) {
928                None | Some(true) => {
929                    return Err(JsNativeError::typ()
930                        .with_message("Proxy trap set unexpected accessor descriptor")
931                        .into());
932                }
933                _ => {}
934            }
935        }
936    }
937
938    // 11. Return true.
939    Ok(true)
940}
941
942/// `10.5.10 [[Delete]] ( P )`
943///
944/// More information:
945///  - [ECMAScript reference][spec]
946///
947/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p
948pub(crate) fn proxy_exotic_delete(
949    obj: &JsObject,
950    key: &PropertyKey,
951    context: &mut InternalMethodPropertyContext<'_>,
952) -> JsResult<bool> {
953    // 1. Let handler be O.[[ProxyHandler]].
954    // 2. If handler is null, throw a TypeError exception.
955    // 3. Assert: Type(handler) is Object.
956    // 4. Let target be O.[[ProxyTarget]].
957    let (target, handler) = obj
958        .downcast_ref::<Proxy>()
959        .js_expect("Proxy object internal internal method called on non-proxy object")?
960        .try_data()?;
961
962    // 5. Let trap be ? GetMethod(handler, "deleteProperty").
963    let Some(trap) = handler.get_method(js_string!("deleteProperty"), context)? else {
964        // 6. If trap is undefined, then
965        // a. Return ? target.[[Delete]](P).
966        return target.__delete__(key, context);
967    };
968
969    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).
970    // 8. If booleanTrapResult is false, return false.
971    if !trap
972        .call(
973            &handler.into(),
974            &[target.clone().into(), key.clone().into()],
975            context,
976        )?
977        .to_boolean()
978    {
979        return Ok(false);
980    }
981
982    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
983    match target.__get_own_property__(key, context)? {
984        // 10. If targetDesc is undefined, return true.
985        None => return Ok(true),
986        // 11. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
987        Some(target_desc) => {
988            if !target_desc.expect_configurable() {
989                return Err(JsNativeError::typ()
990                    .with_message("Proxy trap failed to delete property")
991                    .into());
992            }
993        }
994    }
995
996    // 12. Let extensibleTarget be ? IsExtensible(target).
997    // 13. If extensibleTarget is false, throw a TypeError exception.
998    if !target.is_extensible(context)? {
999        return Err(JsNativeError::typ()
1000            .with_message("Proxy trap failed to delete property")
1001            .into());
1002    }
1003
1004    // 14. Return true.
1005    Ok(true)
1006}
1007
1008/// `10.5.11 [[OwnPropertyKeys]] ( )`
1009///
1010/// More information:
1011///  - [ECMAScript reference][spec]
1012///
1013/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
1014pub(crate) fn proxy_exotic_own_property_keys(
1015    obj: &JsObject,
1016    context: &mut Context,
1017) -> JsResult<Vec<PropertyKey>> {
1018    // 1. Let handler be O.[[ProxyHandler]].
1019    // 2. If handler is null, throw a TypeError exception.
1020    // 3. Assert: Type(handler) is Object.
1021    // 4. Let target be O.[[ProxyTarget]].
1022    let (target, handler) = obj
1023        .downcast_ref::<Proxy>()
1024        .js_expect("Proxy object internal internal method called on non-proxy object")?
1025        .try_data()?;
1026
1027    // 5. Let trap be ? GetMethod(handler, "ownKeys").
1028    let Some(trap) = handler.get_method(js_string!("ownKeys"), context)? else {
1029        // 6. If trap is undefined, then
1030        // a. Return ? target.[[OwnPropertyKeys]]().
1031        return target.__own_property_keys__(context);
1032    };
1033
1034    // 7. Let trapResultArray be ? Call(trap, handler, « target »).
1035    let trap_result_array = trap.call(&handler.into(), &[target.clone().into()], context)?;
1036
1037    // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »).
1038    let trap_result_raw =
1039        trap_result_array.create_list_from_array_like(&[Type::String, Type::Symbol], context)?;
1040
1041    // 9. If trapResult contains any duplicate entries, throw a TypeError exception.
1042    let mut unchecked_result_keys: FxHashSet<PropertyKey> = FxHashSet::default();
1043    let mut trap_result = Vec::new();
1044    for value in &trap_result_raw {
1045        match value.variant() {
1046            JsVariant::String(s) => {
1047                if !unchecked_result_keys.insert(s.clone().into()) {
1048                    return Err(JsNativeError::typ()
1049                        .with_message("Proxy trap result contains duplicate string property keys")
1050                        .into());
1051                }
1052                trap_result.push(s.clone().into());
1053            }
1054            JsVariant::Symbol(s) => {
1055                if !unchecked_result_keys.insert(s.clone().into()) {
1056                    return Err(JsNativeError::typ()
1057                        .with_message("Proxy trap result contains duplicate symbol property keys")
1058                        .into());
1059                }
1060                trap_result.push(s.clone().into());
1061            }
1062            _ => {}
1063        }
1064    }
1065
1066    // 10. Let extensibleTarget be ? IsExtensible(target).
1067    let extensible_target = target.is_extensible(context)?;
1068
1069    // 11. Let targetKeys be ? target.[[OwnPropertyKeys]]().
1070    // 12. Assert: targetKeys is a List of property keys.
1071    // 13. Assert: targetKeys contains no duplicate entries.
1072    let target_keys = target.__own_property_keys__(context)?;
1073
1074    // 14. Let targetConfigurableKeys be a new empty List.
1075    // 15. Let targetNonconfigurableKeys be a new empty List.
1076    let mut target_configurable_keys = Vec::new();
1077    let mut target_nonconfigurable_keys = Vec::new();
1078
1079    // 16. For each element key of targetKeys, do
1080    for key in target_keys {
1081        // a. Let desc be ? target.[[GetOwnProperty]](key).
1082        match target.__get_own_property__(&key, &mut context.into())? {
1083            // b. If desc is not undefined and desc.[[Configurable]] is false, then
1084            Some(desc) if !desc.expect_configurable() => {
1085                // i. Append key as an element of targetNonconfigurableKeys.
1086                target_nonconfigurable_keys.push(key);
1087            }
1088            // c. Else,
1089            _ => {
1090                // i. Append key as an element of targetConfigurableKeys.
1091                target_configurable_keys.push(key);
1092            }
1093        }
1094    }
1095
1096    // 17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then
1097    if extensible_target && target_nonconfigurable_keys.is_empty() {
1098        // a. Return trapResult.
1099        return Ok(trap_result);
1100    }
1101
1102    // 18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult.
1103    // 19. For each element key of targetNonconfigurableKeys, do
1104    for key in target_nonconfigurable_keys {
1105        // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
1106        // b. Remove key from uncheckedResultKeys.
1107        if !unchecked_result_keys.remove(&key) {
1108            return Err(JsNativeError::typ()
1109                .with_message("Proxy trap failed to return all non-configurable property keys")
1110                .into());
1111        }
1112    }
1113
1114    // 20. If extensibleTarget is true, return trapResult.
1115    if extensible_target {
1116        return Ok(trap_result);
1117    }
1118
1119    // 21. For each element key of targetConfigurableKeys, do
1120    for key in target_configurable_keys {
1121        // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
1122        // b. Remove key from uncheckedResultKeys.
1123        if !unchecked_result_keys.remove(&key) {
1124            return Err(JsNativeError::typ()
1125                .with_message("Proxy trap failed to return all configurable property keys")
1126                .into());
1127        }
1128    }
1129
1130    // 22. If uncheckedResultKeys is not empty, throw a TypeError exception.
1131    if !unchecked_result_keys.is_empty() {
1132        return Err(JsNativeError::typ()
1133            .with_message("Proxy trap failed to return all property keys")
1134            .into());
1135    }
1136
1137    // 23. Return trapResult.
1138    Ok(trap_result)
1139}
1140
1141/// `10.5.12 [[Call]] ( thisArgument, argumentsList )`
1142///
1143/// More information:
1144///  - [ECMAScript reference][spec]
1145///
1146/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist
1147fn proxy_exotic_call(
1148    obj: &JsObject,
1149    argument_count: usize,
1150    context: &mut InternalMethodCallContext<'_>,
1151) -> JsResult<CallValue> {
1152    // 1. Let handler be O.[[ProxyHandler]].
1153    // 2. If handler is null, throw a TypeError exception.
1154    // 3. Assert: Type(handler) is Object.
1155    // 4. Let target be O.[[ProxyTarget]].
1156    let (target, handler) = obj
1157        .downcast_ref::<Proxy>()
1158        .js_expect("Proxy object internal internal method called on non-proxy object")?
1159        .try_data()?;
1160
1161    // 5. Let trap be ? GetMethod(handler, "apply").
1162    let Some(trap) = handler.get_method(js_string!("apply"), context)? else {
1163        // 6. If trap is undefined, then
1164        // a. Return ? Call(target, thisArgument, argumentsList).
1165        return Ok(target.__call__(argument_count));
1166    };
1167
1168    let args = context
1169        .vm
1170        .stack
1171        .calling_convention_pop_arguments(argument_count);
1172
1173    // 7. Let argArray be ! CreateArrayFromList(argumentsList).
1174    let arg_array = array::Array::create_array_from_list(args, context);
1175
1176    // 8. Return ? Call(trap, handler, « target, thisArgument, argArray »).
1177    let _func = context.vm.stack.pop();
1178    let this = context.vm.stack.pop();
1179
1180    context.vm.stack.push(handler); // This
1181    context.vm.stack.push(trap.clone()); // Function
1182
1183    context.vm.stack.push(target);
1184    context.vm.stack.push(this);
1185    context.vm.stack.push(arg_array);
1186    Ok(trap.__call__(3))
1187}
1188
1189/// `[[Construct]] ( argumentsList, newTarget )`
1190///
1191/// More information:
1192///  - [ECMAScript reference][spec]
1193///
1194/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget
1195fn proxy_exotic_construct(
1196    obj: &JsObject,
1197    argument_count: usize,
1198    context: &mut InternalMethodCallContext<'_>,
1199) -> JsResult<CallValue> {
1200    // 1. Let handler be O.[[ProxyHandler]].
1201    // 2. If handler is null, throw a TypeError exception.
1202    // 3. Assert: Type(handler) is Object.
1203    // 4. Let target be O.[[ProxyTarget]].
1204    let (target, handler) = obj
1205        .downcast_ref::<Proxy>()
1206        .js_expect("Proxy object internal internal method called on non-proxy object")?
1207        .try_data()?;
1208
1209    // 5. Assert: IsConstructor(target) is true.
1210    assert!(target.is_constructor());
1211
1212    // 6. Let trap be ? GetMethod(handler, "construct").
1213    let Some(trap) = handler.get_method(js_string!("construct"), context)? else {
1214        // 7. If trap is undefined, then
1215        // a. Return ? Construct(target, argumentsList, newTarget).
1216        return Ok(target.__construct__(argument_count));
1217    };
1218
1219    let new_target = context.vm.stack.pop();
1220    let args = context
1221        .vm
1222        .stack
1223        .calling_convention_pop_arguments(argument_count);
1224    let _func = context.vm.stack.pop();
1225    let _this = context.vm.stack.pop();
1226
1227    // 8. Let argArray be ! CreateArrayFromList(argumentsList).
1228    let arg_array = array::Array::create_array_from_list(args, context);
1229
1230    // 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
1231    let new_obj = trap.call(
1232        &handler.into(),
1233        &[target.into(), arg_array.into(), new_target],
1234        context,
1235    )?;
1236
1237    // 10. If Type(newObj) is not Object, throw a TypeError exception.
1238    let new_obj = new_obj.as_object().ok_or_else(|| {
1239        JsNativeError::typ().with_message("Proxy trap constructor returned non-object value")
1240    })?;
1241
1242    // 11. Return newObj.
1243    context.vm.stack.push(new_obj);
1244    Ok(CallValue::Complete)
1245}
1246
1247#[cfg(test)]
1248mod tests;