JSTests:
[WebKit-https.git] / Source / JavaScriptCore / runtime / ProxyObject.cpp
1 /*
2  * Copyright (C) 2016-2017 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ProxyObject.h"
28
29 #include "ArrayConstructor.h"
30 #include "Error.h"
31 #include "IdentifierInlines.h"
32 #include "JSCInlines.h"
33 #include "JSObjectInlines.h"
34 #include "ObjectConstructor.h"
35 #include "SlotVisitorInlines.h"
36 #include "StructureInlines.h"
37 #include "VMInlines.h"
38 #include <wtf/NoTailCalls.h>
39
40 // Note that we use NO_TAIL_CALLS() throughout this file because we rely on the machine stack
41 // growing larger for throwing OOM errors for when we have an effectively cyclic prototype chain.
42
43 namespace JSC {
44
45 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ProxyObject);
46
47 const ClassInfo ProxyObject::s_info = { "ProxyObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ProxyObject) };
48
49 ProxyObject::ProxyObject(VM& vm, Structure* structure)
50     : Base(vm, structure)
51 {
52 }
53
54 String ProxyObject::toStringName(const JSObject* object, ExecState* exec)
55 {
56     VM& vm = exec->vm();
57     auto scope = DECLARE_THROW_SCOPE(vm);
58     const ProxyObject* proxy = jsCast<const ProxyObject*>(object);
59     while (proxy) {
60         const JSObject* target = proxy->target();
61         bool targetIsArray = isArray(exec, target);
62         if (UNLIKELY(scope.exception()))
63             break;
64         if (targetIsArray)
65             RELEASE_AND_RETURN(scope, target->classInfo(vm)->methodTable.toStringName(target, exec));
66
67         proxy = jsDynamicCast<const ProxyObject*>(vm, target);
68     }
69     return "Object"_s;
70 }
71
72 Structure* ProxyObject::structureForTarget(JSGlobalObject* globalObject, JSValue target)
73 {
74     if (!target.isObject())
75         return globalObject->proxyObjectStructure();
76
77     JSObject* targetAsObject = jsCast<JSObject*>(target);
78     CallData ignoredCallData;
79     VM& vm = globalObject->vm();
80     bool isCallable = targetAsObject->methodTable(vm)->getCallData(targetAsObject, ignoredCallData) != CallType::None;
81     return isCallable ? globalObject->callableProxyObjectStructure() : globalObject->proxyObjectStructure();
82 }
83
84 void ProxyObject::finishCreation(VM& vm, ExecState* exec, JSValue target, JSValue handler)
85 {
86     auto scope = DECLARE_THROW_SCOPE(vm);
87     Base::finishCreation(vm);
88     ASSERT(type() == ProxyObjectType);
89     if (!target.isObject()) {
90         throwTypeError(exec, scope, "A Proxy's 'target' should be an Object"_s);
91         return;
92     }
93     if (ProxyObject* targetAsProxy = jsDynamicCast<ProxyObject*>(vm, target)) {
94         if (targetAsProxy->handler().isNull()) {
95             throwTypeError(exec, scope, "If a Proxy's handler is another Proxy object, the other Proxy should not have been revoked"_s);
96             return;
97         }
98     }
99     if (!handler.isObject()) {
100         throwTypeError(exec, scope, "A Proxy's 'handler' should be an Object"_s);
101         return;
102     }
103
104     JSObject* targetAsObject = jsCast<JSObject*>(target);
105
106     CallData ignoredCallData;
107     m_isCallable = targetAsObject->methodTable(vm)->getCallData(targetAsObject, ignoredCallData) != CallType::None;
108     if (m_isCallable) {
109         TypeInfo info = structure(vm)->typeInfo();
110         RELEASE_ASSERT(info.implementsHasInstance() && info.implementsDefaultHasInstance());
111     }
112
113     m_isConstructible = jsCast<JSObject*>(target)->isConstructor(vm);
114
115     m_target.set(vm, this, targetAsObject);
116     m_handler.set(vm, this, handler);
117 }
118
119 static const ASCIILiteral s_proxyAlreadyRevokedErrorMessage { "Proxy has already been revoked. No more operations are allowed to be performed on it"_s };
120
121 static JSValue performProxyGet(ExecState* exec, ProxyObject* proxyObject, JSValue receiver, PropertyName propertyName)
122 {
123     NO_TAIL_CALLS();
124
125     VM& vm = exec->vm();
126     auto scope = DECLARE_THROW_SCOPE(vm);
127     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
128         throwStackOverflowError(exec, scope);
129         return { };
130     }
131
132     JSObject* target = proxyObject->target();
133
134     auto performDefaultGet = [&] {
135         scope.release();
136         PropertySlot slot(receiver, PropertySlot::InternalMethodType::Get);
137         bool hasProperty = target->getPropertySlot(exec, propertyName, slot);
138         EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
139         if (hasProperty)
140             RELEASE_AND_RETURN(scope, slot.getValue(exec, propertyName));
141
142         return jsUndefined();
143     };
144
145     if (propertyName.isPrivateName())
146         return performDefaultGet();
147
148     JSValue handlerValue = proxyObject->handler();
149     if (handlerValue.isNull())
150         return throwTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
151
152     JSObject* handler = jsCast<JSObject*>(handlerValue);
153     CallData callData;
154     CallType callType;
155     JSValue getHandler = handler->getMethod(exec, callData, callType, vm.propertyNames->get, "'get' property of a Proxy's handler object should be callable"_s);
156     RETURN_IF_EXCEPTION(scope, { });
157
158     if (getHandler.isUndefined())
159         return performDefaultGet();
160
161     MarkedArgumentBuffer arguments;
162     arguments.append(target);
163     arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
164     arguments.append(receiver);
165     ASSERT(!arguments.hasOverflowed());
166     JSValue trapResult = call(exec, getHandler, callType, callData, handler, arguments);
167     RETURN_IF_EXCEPTION(scope, { });
168
169     PropertyDescriptor descriptor;
170     if (target->getOwnPropertyDescriptor(exec, propertyName, descriptor)) {
171         if (descriptor.isDataDescriptor() && !descriptor.configurable() && !descriptor.writable()) {
172             if (!sameValue(exec, descriptor.value(), trapResult))
173                 return throwTypeError(exec, scope, "Proxy handler's 'get' result of a non-configurable and non-writable property should be the same value as the target's property"_s);
174         } else if (descriptor.isAccessorDescriptor() && !descriptor.configurable() && descriptor.getter().isUndefined()) {
175             if (!trapResult.isUndefined())
176                 return throwTypeError(exec, scope, "Proxy handler's 'get' result of a non-configurable accessor property without a getter should be undefined"_s);
177         }
178     }
179
180     RETURN_IF_EXCEPTION(scope, { });
181
182     return trapResult;
183 }
184
185 bool ProxyObject::performGet(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
186 {
187     NO_TAIL_CALLS();
188
189     VM& vm = exec->vm();
190     auto scope = DECLARE_THROW_SCOPE(vm);
191     JSValue result = performProxyGet(exec, this, slot.thisValue(), propertyName);
192     RETURN_IF_EXCEPTION(scope, false);
193     unsigned ignoredAttributes = 0;
194     slot.setValue(this, ignoredAttributes, result);
195     return true;
196 }
197
198 bool ProxyObject::performInternalMethodGetOwnProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
199 {
200     NO_TAIL_CALLS();
201
202     VM& vm = exec->vm();
203     auto scope = DECLARE_THROW_SCOPE(vm);
204     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
205         throwStackOverflowError(exec, scope);
206         return false;
207     }
208     JSObject* target = this->target();
209
210     auto performDefaultGetOwnProperty = [&] {
211         return target->methodTable(vm)->getOwnPropertySlot(target, exec, propertyName, slot);
212     };
213
214     if (propertyName.isPrivateName())
215         RELEASE_AND_RETURN(scope, performDefaultGetOwnProperty());
216
217     JSValue handlerValue = this->handler();
218     if (handlerValue.isNull()) {
219         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
220         return false;
221     }
222
223     JSObject* handler = jsCast<JSObject*>(handlerValue);
224     CallData callData;
225     CallType callType;
226     JSValue getOwnPropertyDescriptorMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "getOwnPropertyDescriptor"), "'getOwnPropertyDescriptor' property of a Proxy's handler should be callable"_s);
227     RETURN_IF_EXCEPTION(scope, false);
228     if (getOwnPropertyDescriptorMethod.isUndefined())
229         RELEASE_AND_RETURN(scope, performDefaultGetOwnProperty());
230
231     MarkedArgumentBuffer arguments;
232     arguments.append(target);
233     arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
234     ASSERT(!arguments.hasOverflowed());
235     JSValue trapResult = call(exec, getOwnPropertyDescriptorMethod, callType, callData, handler, arguments);
236     RETURN_IF_EXCEPTION(scope, false);
237
238     if (!trapResult.isUndefined() && !trapResult.isObject()) {
239         throwVMTypeError(exec, scope, "result of 'getOwnPropertyDescriptor' call should either be an Object or undefined"_s);
240         return false;
241     }
242
243     PropertyDescriptor targetPropertyDescriptor;
244     bool isTargetPropertyDescriptorDefined = target->getOwnPropertyDescriptor(exec, propertyName, targetPropertyDescriptor);
245     RETURN_IF_EXCEPTION(scope, false);
246
247     if (trapResult.isUndefined()) {
248         if (!isTargetPropertyDescriptorDefined)
249             return false;
250         if (!targetPropertyDescriptor.configurable()) {
251             throwVMTypeError(exec, scope, "When the result of 'getOwnPropertyDescriptor' is undefined the target must be configurable"_s);
252             return false;
253         }
254         // FIXME: this doesn't work if 'target' is another Proxy. We don't have isExtensible implemented in a way that fits w/ Proxys.
255         // https://bugs.webkit.org/show_bug.cgi?id=154375
256         bool isExtensible = target->isExtensible(exec);
257         RETURN_IF_EXCEPTION(scope, false);
258         if (!isExtensible) {
259             // FIXME: Come up with a test for this error. I'm not sure how to because
260             // Object.seal(o) will make all fields [[Configurable]] false.
261             // https://bugs.webkit.org/show_bug.cgi?id=154376
262             throwVMTypeError(exec, scope, "When 'getOwnPropertyDescriptor' returns undefined, the 'target' of a Proxy should be extensible"_s);
263             return false;
264         }
265
266         return false;
267     }
268
269     bool isExtensible = target->isExtensible(exec);
270     RETURN_IF_EXCEPTION(scope, false);
271     PropertyDescriptor trapResultAsDescriptor;
272     toPropertyDescriptor(exec, trapResult, trapResultAsDescriptor);
273     RETURN_IF_EXCEPTION(scope, false);
274     bool throwException = false;
275     bool valid = validateAndApplyPropertyDescriptor(exec, nullptr, propertyName, isExtensible,
276         trapResultAsDescriptor, isTargetPropertyDescriptorDefined, targetPropertyDescriptor, throwException);
277     RETURN_IF_EXCEPTION(scope, false);
278     if (!valid) {
279         throwVMTypeError(exec, scope, "Result from 'getOwnPropertyDescriptor' fails the IsCompatiblePropertyDescriptor test"_s);
280         return false;
281     }
282
283     if (!trapResultAsDescriptor.configurable()) {
284         if (!isTargetPropertyDescriptorDefined || targetPropertyDescriptor.configurable()) {
285             throwVMTypeError(exec, scope, "Result from 'getOwnPropertyDescriptor' can't be non-configurable when the 'target' doesn't have it as an own property or if it is a configurable own property on 'target'"_s);
286             return false;
287         }
288     }
289
290     if (trapResultAsDescriptor.isAccessorDescriptor()) {
291         GetterSetter* getterSetter = trapResultAsDescriptor.slowGetterSetter(exec);
292         RETURN_IF_EXCEPTION(scope, false);
293         slot.setGetterSlot(this, trapResultAsDescriptor.attributes(), getterSetter);
294     } else if (trapResultAsDescriptor.isDataDescriptor() && !trapResultAsDescriptor.value().isEmpty())
295         slot.setValue(this, trapResultAsDescriptor.attributes(), trapResultAsDescriptor.value());
296     else
297         slot.setValue(this, trapResultAsDescriptor.attributes(), jsUndefined()); // We use undefined because it's the default value in object properties.
298
299     return true;
300 }
301
302 bool ProxyObject::performHasProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
303 {
304     NO_TAIL_CALLS();
305
306     VM& vm = exec->vm();
307     auto scope = DECLARE_THROW_SCOPE(vm);
308     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
309         throwStackOverflowError(exec, scope);
310         return false;
311     }
312     JSObject* target = this->target();
313     slot.setValue(this, static_cast<unsigned>(PropertyAttribute::None), jsUndefined()); // Nobody should rely on our value, but be safe and protect against any bad actors reading our value.
314
315     auto performDefaultHasProperty = [&] {
316         return target->methodTable(vm)->getOwnPropertySlot(target, exec, propertyName, slot);
317     };
318
319     if (propertyName.isPrivateName())
320         RELEASE_AND_RETURN(scope, performDefaultHasProperty());
321
322     JSValue handlerValue = this->handler();
323     if (handlerValue.isNull()) {
324         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
325         return false;
326     }
327
328     JSObject* handler = jsCast<JSObject*>(handlerValue);
329     CallData callData;
330     CallType callType;
331     JSValue hasMethod = handler->getMethod(exec, callData, callType, vm.propertyNames->has, "'has' property of a Proxy's handler should be callable"_s);
332     RETURN_IF_EXCEPTION(scope, false);
333     if (hasMethod.isUndefined())
334         RELEASE_AND_RETURN(scope, performDefaultHasProperty());
335
336     MarkedArgumentBuffer arguments;
337     arguments.append(target);
338     arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
339     ASSERT(!arguments.hasOverflowed());
340     JSValue trapResult = call(exec, hasMethod, callType, callData, handler, arguments);
341     RETURN_IF_EXCEPTION(scope, false);
342
343     bool trapResultAsBool = trapResult.toBoolean(exec);
344     RETURN_IF_EXCEPTION(scope, false);
345
346     if (!trapResultAsBool) {
347         PropertyDescriptor descriptor;
348         bool isPropertyDescriptorDefined = target->getOwnPropertyDescriptor(exec, propertyName, descriptor); 
349         RETURN_IF_EXCEPTION(scope, false);
350         if (isPropertyDescriptorDefined) {
351             if (!descriptor.configurable()) {
352                 throwVMTypeError(exec, scope, "Proxy 'has' must return 'true' for non-configurable properties"_s);
353                 return false;
354             }
355             bool isExtensible = target->isExtensible(exec);
356             RETURN_IF_EXCEPTION(scope, false);
357             if (!isExtensible) {
358                 throwVMTypeError(exec, scope, "Proxy 'has' must return 'true' for a non-extensible 'target' object with a configurable property"_s);
359                 return false;
360             }
361         }
362     }
363
364     return trapResultAsBool;
365 }
366
367 bool ProxyObject::getOwnPropertySlotCommon(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
368 {
369     slot.disableCaching();
370     slot.setIsTaintedByOpaqueObject();
371
372     if (slot.internalMethodType() == PropertySlot::InternalMethodType::VMInquiry)
373         return false;
374
375     VM& vm = exec->vm();
376     auto scope = DECLARE_THROW_SCOPE(vm);
377     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
378         throwStackOverflowError(exec, scope);
379         return false;
380     }
381     switch (slot.internalMethodType()) {
382     case PropertySlot::InternalMethodType::Get:
383         RELEASE_AND_RETURN(scope, performGet(exec, propertyName, slot));
384     case PropertySlot::InternalMethodType::GetOwnProperty:
385         RELEASE_AND_RETURN(scope, performInternalMethodGetOwnProperty(exec, propertyName, slot));
386     case PropertySlot::InternalMethodType::HasProperty:
387         RELEASE_AND_RETURN(scope, performHasProperty(exec, propertyName, slot));
388     default:
389         return false;
390     }
391
392     RELEASE_ASSERT_NOT_REACHED();
393     return false;
394 }
395
396 bool ProxyObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
397 {
398     ProxyObject* thisObject = jsCast<ProxyObject*>(object);
399     return thisObject->getOwnPropertySlotCommon(exec, propertyName, slot);
400 }
401
402 bool ProxyObject::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot)
403 {
404     ProxyObject* thisObject = jsCast<ProxyObject*>(object);
405     Identifier ident = Identifier::from(exec, propertyName); 
406     return thisObject->getOwnPropertySlotCommon(exec, ident.impl(), slot);
407 }
408
409 template <typename PerformDefaultPutFunction>
410 bool ProxyObject::performPut(ExecState* exec, JSValue putValue, JSValue thisValue, PropertyName propertyName, PerformDefaultPutFunction performDefaultPut)
411 {
412     NO_TAIL_CALLS();
413
414     VM& vm = exec->vm();
415     auto scope = DECLARE_THROW_SCOPE(vm);
416     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
417         throwStackOverflowError(exec, scope);
418         return false;
419     }
420
421     if (propertyName.isPrivateName())
422         RELEASE_AND_RETURN(scope, performDefaultPut());
423
424     JSValue handlerValue = this->handler();
425     if (handlerValue.isNull()) {
426         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
427         return false;
428     }
429
430     JSObject* handler = jsCast<JSObject*>(handlerValue);
431     CallData callData;
432     CallType callType;
433     JSValue setMethod = handler->getMethod(exec, callData, callType, vm.propertyNames->set, "'set' property of a Proxy's handler should be callable"_s);
434     RETURN_IF_EXCEPTION(scope, false);
435     JSObject* target = this->target();
436     if (setMethod.isUndefined())
437         RELEASE_AND_RETURN(scope, performDefaultPut());
438
439     MarkedArgumentBuffer arguments;
440     arguments.append(target);
441     arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
442     arguments.append(putValue);
443     arguments.append(thisValue);
444     ASSERT(!arguments.hasOverflowed());
445     JSValue trapResult = call(exec, setMethod, callType, callData, handler, arguments);
446     RETURN_IF_EXCEPTION(scope, false);
447     bool trapResultAsBool = trapResult.toBoolean(exec);
448     RETURN_IF_EXCEPTION(scope, false);
449     if (!trapResultAsBool)
450         return false;
451
452     PropertyDescriptor descriptor;
453     bool hasProperty = target->getOwnPropertyDescriptor(exec, propertyName, descriptor);
454     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
455     if (hasProperty) {
456         if (descriptor.isDataDescriptor() && !descriptor.configurable() && !descriptor.writable()) {
457             if (!sameValue(exec, descriptor.value(), putValue)) {
458                 throwVMTypeError(exec, scope, "Proxy handler's 'set' on a non-configurable and non-writable property on 'target' should either return false or be the same value already on the 'target'"_s);
459                 return false;
460             }
461         } else if (descriptor.isAccessorDescriptor() && !descriptor.configurable() && descriptor.setter().isUndefined()) {
462             throwVMTypeError(exec, scope, "Proxy handler's 'set' method on a non-configurable accessor property without a setter should return false"_s);
463             return false;
464         }
465     }
466     return true;
467 }
468
469 bool ProxyObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
470 {
471     VM& vm = exec->vm();
472     slot.disableCaching();
473
474     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
475     auto performDefaultPut = [&] () {
476         JSObject* target = jsCast<JSObject*>(thisObject->target());
477         return target->methodTable(vm)->put(target, exec, propertyName, value, slot);
478     };
479     return thisObject->performPut(exec, value, slot.thisValue(), propertyName, performDefaultPut);
480 }
481
482 bool ProxyObject::putByIndexCommon(ExecState* exec, JSValue thisValue, unsigned propertyName, JSValue putValue, bool shouldThrow)
483 {
484     VM& vm = exec->vm();
485     auto scope = DECLARE_THROW_SCOPE(vm);
486     Identifier ident = Identifier::from(exec, propertyName);
487     RETURN_IF_EXCEPTION(scope, false);
488     auto performDefaultPut = [&] () {
489         JSObject* target = this->target();
490         bool isStrictMode = shouldThrow;
491         PutPropertySlot slot(thisValue, isStrictMode); // We must preserve the "this" target of the putByIndex.
492         return target->methodTable(vm)->put(target, exec, ident.impl(), putValue, slot);
493     };
494     RELEASE_AND_RETURN(scope, performPut(exec, putValue, thisValue, ident.impl(), performDefaultPut));
495 }
496
497 bool ProxyObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
498 {
499     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
500     return thisObject->putByIndexCommon(exec, thisObject, propertyName, value, shouldThrow);
501 }
502
503 static EncodedJSValue JSC_HOST_CALL performProxyCall(ExecState* exec)
504 {
505     NO_TAIL_CALLS();
506
507     VM& vm = exec->vm();
508     auto scope = DECLARE_THROW_SCOPE(vm);
509     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
510         throwStackOverflowError(exec, scope);
511         return encodedJSValue();
512     }
513     ProxyObject* proxy = jsCast<ProxyObject*>(exec->jsCallee());
514     JSValue handlerValue = proxy->handler();
515     if (handlerValue.isNull())
516         return throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
517
518     JSObject* handler = jsCast<JSObject*>(handlerValue);
519     CallData callData;
520     CallType callType;
521     JSValue applyMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "apply"), "'apply' property of a Proxy's handler should be callable"_s);
522     RETURN_IF_EXCEPTION(scope, encodedJSValue());
523     JSObject* target = proxy->target();
524     if (applyMethod.isUndefined()) {
525         CallData callData;
526         CallType callType = target->methodTable(vm)->getCallData(target, callData);
527         RELEASE_ASSERT(callType != CallType::None);
528         RELEASE_AND_RETURN(scope, JSValue::encode(call(exec, target, callType, callData, exec->thisValue(), ArgList(exec))));
529     }
530
531     JSArray* argArray = constructArray(exec, static_cast<ArrayAllocationProfile*>(nullptr), ArgList(exec));
532     RETURN_IF_EXCEPTION(scope, encodedJSValue());
533     MarkedArgumentBuffer arguments;
534     arguments.append(target);
535     arguments.append(exec->thisValue().toThis(exec, ECMAMode::StrictMode));
536     arguments.append(argArray);
537     ASSERT(!arguments.hasOverflowed());
538     RELEASE_AND_RETURN(scope, JSValue::encode(call(exec, applyMethod, callType, callData, handler, arguments)));
539 }
540
541 CallType ProxyObject::getCallData(JSCell* cell, CallData& callData)
542 {
543     ProxyObject* proxy = jsCast<ProxyObject*>(cell);
544     if (!proxy->m_isCallable) {
545         callData.js.functionExecutable = nullptr;
546         callData.js.scope = nullptr;
547         return CallType::None;
548     }
549
550     callData.native.function = performProxyCall;
551     return CallType::Host;
552 }
553
554 static EncodedJSValue JSC_HOST_CALL performProxyConstruct(ExecState* exec)
555 {
556     NO_TAIL_CALLS();
557
558     VM& vm = exec->vm();
559     auto scope = DECLARE_THROW_SCOPE(vm);
560     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
561         throwStackOverflowError(exec, scope);
562         return encodedJSValue();
563     }
564     ProxyObject* proxy = jsCast<ProxyObject*>(exec->jsCallee());
565     JSValue handlerValue = proxy->handler();
566     if (handlerValue.isNull())
567         return throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
568
569     JSObject* handler = jsCast<JSObject*>(handlerValue);
570     CallData callData;
571     CallType callType;
572     JSValue constructMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "construct"), "'construct' property of a Proxy's handler should be constructible"_s);
573     RETURN_IF_EXCEPTION(scope, encodedJSValue());
574     JSObject* target = proxy->target();
575     if (constructMethod.isUndefined()) {
576         ConstructData constructData;
577         ConstructType constructType = target->methodTable(vm)->getConstructData(target, constructData);
578         RELEASE_ASSERT(constructType != ConstructType::None);
579         RELEASE_AND_RETURN(scope, JSValue::encode(construct(exec, target, constructType, constructData, ArgList(exec), exec->newTarget())));
580     }
581
582     JSArray* argArray = constructArray(exec, static_cast<ArrayAllocationProfile*>(nullptr), ArgList(exec));
583     RETURN_IF_EXCEPTION(scope, encodedJSValue());
584     MarkedArgumentBuffer arguments;
585     arguments.append(target);
586     arguments.append(argArray);
587     arguments.append(exec->newTarget());
588     ASSERT(!arguments.hasOverflowed());
589     JSValue result = call(exec, constructMethod, callType, callData, handler, arguments);
590     RETURN_IF_EXCEPTION(scope, encodedJSValue());
591     if (!result.isObject())
592         return throwVMTypeError(exec, scope, "Result from Proxy handler's 'construct' method should be an object"_s);
593     return JSValue::encode(result);
594 }
595
596 ConstructType ProxyObject::getConstructData(JSCell* cell, ConstructData& constructData)
597 {
598     ProxyObject* proxy = jsCast<ProxyObject*>(cell);
599     if (!proxy->m_isConstructible) {
600         constructData.js.functionExecutable = nullptr;
601         constructData.js.scope = nullptr;
602         return ConstructType::None;
603     }
604
605     constructData.native.function = performProxyConstruct;
606     return ConstructType::Host;
607 }
608
609 template <typename DefaultDeleteFunction>
610 bool ProxyObject::performDelete(ExecState* exec, PropertyName propertyName, DefaultDeleteFunction performDefaultDelete)
611 {
612     NO_TAIL_CALLS();
613
614     VM& vm = exec->vm();
615     auto scope = DECLARE_THROW_SCOPE(vm);
616     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
617         throwStackOverflowError(exec, scope);
618         return false;
619     }
620
621     if (propertyName.isPrivateName())
622         RELEASE_AND_RETURN(scope, performDefaultDelete());
623
624     JSValue handlerValue = this->handler();
625     if (handlerValue.isNull()) {
626         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
627         return false;
628     }
629
630     JSObject* handler = jsCast<JSObject*>(handlerValue);
631     CallData callData;
632     CallType callType;
633     JSValue deletePropertyMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "deleteProperty"), "'deleteProperty' property of a Proxy's handler should be callable"_s);
634     RETURN_IF_EXCEPTION(scope, false);
635     JSObject* target = this->target();
636     if (deletePropertyMethod.isUndefined())
637         RELEASE_AND_RETURN(scope, performDefaultDelete());
638
639     MarkedArgumentBuffer arguments;
640     arguments.append(target);
641     arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
642     ASSERT(!arguments.hasOverflowed());
643     JSValue trapResult = call(exec, deletePropertyMethod, callType, callData, handler, arguments);
644     RETURN_IF_EXCEPTION(scope, false);
645
646     bool trapResultAsBool = trapResult.toBoolean(exec);
647     RETURN_IF_EXCEPTION(scope, false);
648
649     if (!trapResultAsBool)
650         return false;
651
652     PropertyDescriptor descriptor;
653     if (target->getOwnPropertyDescriptor(exec, propertyName, descriptor)) {
654         if (!descriptor.configurable()) {
655             throwVMTypeError(exec, scope, "Proxy handler's 'deleteProperty' method should return false when the target's property is not configurable"_s);
656             return false;
657         }
658     }
659
660     RETURN_IF_EXCEPTION(scope, false);
661
662     return true;
663 }
664
665 bool ProxyObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
666 {
667     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
668     auto performDefaultDelete = [&] () -> bool {
669         JSObject* target = thisObject->target();
670         return target->methodTable(exec->vm())->deleteProperty(target, exec, propertyName);
671     };
672     return thisObject->performDelete(exec, propertyName, performDefaultDelete);
673 }
674
675 bool ProxyObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
676 {
677     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
678     Identifier ident = Identifier::from(exec, propertyName); 
679     auto performDefaultDelete = [&] () -> bool {
680         JSObject* target = thisObject->target();
681         return target->methodTable(exec->vm())->deletePropertyByIndex(target, exec, propertyName);
682     };
683     return thisObject->performDelete(exec, ident.impl(), performDefaultDelete);
684 }
685
686 bool ProxyObject::performPreventExtensions(ExecState* exec)
687 {
688     NO_TAIL_CALLS();
689
690     VM& vm = exec->vm();
691     auto scope = DECLARE_THROW_SCOPE(vm);
692     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
693         throwStackOverflowError(exec, scope);
694         return false;
695     }
696
697     JSValue handlerValue = this->handler();
698     if (handlerValue.isNull()) {
699         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
700         return false;
701     }
702
703     JSObject* handler = jsCast<JSObject*>(handlerValue);
704     CallData callData;
705     CallType callType;
706     JSValue preventExtensionsMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "preventExtensions"), "'preventExtensions' property of a Proxy's handler should be callable"_s);
707     RETURN_IF_EXCEPTION(scope, false);
708     JSObject* target = this->target();
709     if (preventExtensionsMethod.isUndefined())
710         RELEASE_AND_RETURN(scope, target->methodTable(vm)->preventExtensions(target, exec));
711
712     MarkedArgumentBuffer arguments;
713     arguments.append(target);
714     ASSERT(!arguments.hasOverflowed());
715     JSValue trapResult = call(exec, preventExtensionsMethod, callType, callData, handler, arguments);
716     RETURN_IF_EXCEPTION(scope, false);
717
718     bool trapResultAsBool = trapResult.toBoolean(exec);
719     RETURN_IF_EXCEPTION(scope, false);
720
721     if (trapResultAsBool) {
722         bool targetIsExtensible = target->isExtensible(exec);
723         RETURN_IF_EXCEPTION(scope, false);
724         if (targetIsExtensible) {
725             throwVMTypeError(exec, scope, "Proxy's 'preventExtensions' trap returned true even though its target is extensible. It should have returned false"_s);
726             return false;
727         }
728     }
729
730     return trapResultAsBool;
731 }
732
733 bool ProxyObject::preventExtensions(JSObject* object, ExecState* exec)
734 {
735     return jsCast<ProxyObject*>(object)->performPreventExtensions(exec);
736 }
737
738 bool ProxyObject::performIsExtensible(ExecState* exec)
739 {
740     NO_TAIL_CALLS();
741
742     VM& vm = exec->vm();
743     auto scope = DECLARE_THROW_SCOPE(vm);
744     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
745         throwStackOverflowError(exec, scope);
746         return false;
747     }
748
749     JSValue handlerValue = this->handler();
750     if (handlerValue.isNull()) {
751         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
752         return false;
753     }
754
755     JSObject* handler = jsCast<JSObject*>(handlerValue);
756     CallData callData;
757     CallType callType;
758     JSValue isExtensibleMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "isExtensible"), "'isExtensible' property of a Proxy's handler should be callable"_s);
759     RETURN_IF_EXCEPTION(scope, false);
760
761     JSObject* target = this->target();
762     if (isExtensibleMethod.isUndefined())
763         RELEASE_AND_RETURN(scope, target->isExtensible(exec));
764
765     MarkedArgumentBuffer arguments;
766     arguments.append(target);
767     ASSERT(!arguments.hasOverflowed());
768     JSValue trapResult = call(exec, isExtensibleMethod, callType, callData, handler, arguments);
769     RETURN_IF_EXCEPTION(scope, false);
770
771     bool trapResultAsBool = trapResult.toBoolean(exec);
772     RETURN_IF_EXCEPTION(scope, false);
773
774     bool isTargetExtensible = target->isExtensible(exec);
775     RETURN_IF_EXCEPTION(scope, false);
776
777     if (trapResultAsBool != isTargetExtensible) {
778         if (isTargetExtensible) {
779             ASSERT(!trapResultAsBool);
780             throwVMTypeError(exec, scope, "Proxy object's 'isExtensible' trap returned false when the target is extensible. It should have returned true"_s);
781         } else {
782             ASSERT(!isTargetExtensible);
783             ASSERT(trapResultAsBool);
784             throwVMTypeError(exec, scope, "Proxy object's 'isExtensible' trap returned true when the target is non-extensible. It should have returned false"_s);
785         }
786     }
787     
788     return trapResultAsBool;
789 }
790
791 bool ProxyObject::isExtensible(JSObject* object, ExecState* exec)
792 {
793     return jsCast<ProxyObject*>(object)->performIsExtensible(exec);
794 }
795
796 bool ProxyObject::performDefineOwnProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
797 {
798     NO_TAIL_CALLS();
799
800     VM& vm = exec->vm();
801     auto scope = DECLARE_THROW_SCOPE(vm);
802     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
803         throwStackOverflowError(exec, scope);
804         return false;
805     }
806
807     JSObject* target = this->target();
808     auto performDefaultDefineOwnProperty = [&] {
809         RELEASE_AND_RETURN(scope, target->methodTable(vm)->defineOwnProperty(target, exec, propertyName, descriptor, shouldThrow));
810     };
811
812     if (propertyName.isPrivateName())
813         return performDefaultDefineOwnProperty();
814
815     JSValue handlerValue = this->handler();
816     if (handlerValue.isNull()) {
817         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
818         return false;
819     }
820
821     JSObject* handler = jsCast<JSObject*>(handlerValue);
822     CallData callData;
823     CallType callType;
824     JSValue definePropertyMethod = handler->getMethod(exec, callData, callType, vm.propertyNames->defineProperty, "'defineProperty' property of a Proxy's handler should be callable"_s);
825     RETURN_IF_EXCEPTION(scope, false);
826
827     if (definePropertyMethod.isUndefined())
828         return performDefaultDefineOwnProperty();
829
830     JSObject* descriptorObject = constructObjectFromPropertyDescriptor(exec, descriptor);
831     RETURN_IF_EXCEPTION(scope, false);
832
833     MarkedArgumentBuffer arguments;
834     arguments.append(target);
835     arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
836     arguments.append(descriptorObject);
837     ASSERT(!arguments.hasOverflowed());
838     JSValue trapResult = call(exec, definePropertyMethod, callType, callData, handler, arguments);
839     RETURN_IF_EXCEPTION(scope, false);
840
841     bool trapResultAsBool = trapResult.toBoolean(exec);
842     RETURN_IF_EXCEPTION(scope, false);
843
844     if (!trapResultAsBool)
845         return false;
846
847     PropertyDescriptor targetDescriptor;
848     bool isTargetDescriptorDefined = target->getOwnPropertyDescriptor(exec, propertyName, targetDescriptor);
849     RETURN_IF_EXCEPTION(scope, false);
850
851     bool targetIsExtensible = target->isExtensible(exec);
852     RETURN_IF_EXCEPTION(scope, false);
853     bool settingConfigurableToFalse = descriptor.configurablePresent() && !descriptor.configurable();
854
855     if (!isTargetDescriptorDefined) {
856         if (!targetIsExtensible) {
857             throwVMTypeError(exec, scope, "Proxy's 'defineProperty' trap returned true even though getOwnPropertyDescriptor of the Proxy's target returned undefined and the target is non-extensible"_s);
858             return false;
859         }
860         if (settingConfigurableToFalse) {
861             throwVMTypeError(exec, scope, "Proxy's 'defineProperty' trap returned true for a non-configurable field even though getOwnPropertyDescriptor of the Proxy's target returned undefined"_s);
862             return false;
863         }
864
865         return true;
866     } 
867
868     ASSERT(isTargetDescriptorDefined);
869     bool isCurrentDefined = isTargetDescriptorDefined;
870     const PropertyDescriptor& current = targetDescriptor;
871     bool throwException = false;
872     bool isCompatibleDescriptor = validateAndApplyPropertyDescriptor(exec, nullptr, propertyName, targetIsExtensible, descriptor, isCurrentDefined, current, throwException);
873     RETURN_IF_EXCEPTION(scope, false);    
874     if (!isCompatibleDescriptor) {
875         throwVMTypeError(exec, scope, "Proxy's 'defineProperty' trap did not define a property on its target that is compatible with the trap's input descriptor"_s);
876         return false;
877     }
878     if (settingConfigurableToFalse && targetDescriptor.configurable()) {
879         throwVMTypeError(exec, scope, "Proxy's 'defineProperty' trap did not define a non-configurable property on its target even though the input descriptor to the trap said it must do so"_s);
880         return false;
881     }
882     
883     return true;
884 }
885
886 bool ProxyObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
887 {
888     ProxyObject* thisObject = jsCast<ProxyObject*>(object);
889     return thisObject->performDefineOwnProperty(exec, propertyName, descriptor, shouldThrow);
890 }
891
892 void ProxyObject::performGetOwnPropertyNames(ExecState* exec, PropertyNameArray& trapResult, EnumerationMode enumerationMode)
893 {
894     NO_TAIL_CALLS();
895
896     VM& vm = exec->vm();
897     auto scope = DECLARE_THROW_SCOPE(vm);
898     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
899         throwStackOverflowError(exec, scope);
900         return;
901     }
902     JSValue handlerValue = this->handler();
903     if (handlerValue.isNull()) {
904         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
905         return;
906     }
907
908     JSObject* handler = jsCast<JSObject*>(handlerValue);
909     CallData callData;
910     CallType callType;
911     JSValue ownKeysMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "ownKeys"), "'ownKeys' property of a Proxy's handler should be callable"_s);
912     RETURN_IF_EXCEPTION(scope, void());
913     JSObject* target = this->target();
914     if (ownKeysMethod.isUndefined()) {
915         scope.release();
916         target->methodTable(vm)->getOwnPropertyNames(target, exec, trapResult, enumerationMode);
917         return;
918     }
919
920     MarkedArgumentBuffer arguments;
921     arguments.append(target);
922     ASSERT(!arguments.hasOverflowed());
923     JSValue arrayLikeObject = call(exec, ownKeysMethod, callType, callData, handler, arguments);
924     RETURN_IF_EXCEPTION(scope, void());
925
926     PropertyNameMode propertyNameMode = trapResult.propertyNameMode();
927     RuntimeTypeMask resultFilter = 0;
928     switch (propertyNameMode) {
929     case PropertyNameMode::Symbols:
930         resultFilter = TypeSymbol;
931         break;
932     case PropertyNameMode::Strings:
933         resultFilter = TypeString;
934         break;
935     case PropertyNameMode::StringsAndSymbols:
936         resultFilter = TypeSymbol | TypeString;
937         break;
938     }
939     ASSERT(resultFilter);
940     RuntimeTypeMask dontThrowAnExceptionTypeFilter = TypeString | TypeSymbol;
941     HashSet<UniquedStringImpl*> uncheckedResultKeys;
942     HashSet<UniquedStringImpl*> seenKeys;
943
944     auto addPropName = [&] (JSValue value, RuntimeType type) -> bool {
945         static const bool doExitEarly = true;
946         static const bool dontExitEarly = false;
947
948         Identifier ident = value.toPropertyKey(exec);
949         RETURN_IF_EXCEPTION(scope, doExitEarly);
950
951         // If trapResult contains any duplicate entries, throw a TypeError exception.
952         //    
953         // Per spec[1], filtering by type should occur _after_ [[OwnPropertyKeys]], so duplicates
954         // are tracked in a separate hashtable from uncheckedResultKeys (which only contain the
955         // keys filtered by type).
956         //
957         // [1] Per https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeysmust not contain any duplicate names"_s);
958         if (!seenKeys.add(ident.impl()).isNewEntry) {
959             throwTypeError(exec, scope, "Proxy handler's 'ownKeys' trap result must not contain any duplicate names"_s);
960             return doExitEarly;
961         }
962
963         if (!(type & resultFilter))
964             return dontExitEarly;
965
966         uncheckedResultKeys.add(ident.impl());
967         trapResult.addUnchecked(ident.impl());
968         return dontExitEarly;
969     };
970
971     createListFromArrayLike(exec, arrayLikeObject, dontThrowAnExceptionTypeFilter, "Proxy handler's 'ownKeys' method must return an array-like object containing only Strings and Symbols"_s, addPropName);
972     RETURN_IF_EXCEPTION(scope, void());
973
974     bool targetIsExensible = target->isExtensible(exec);
975     RETURN_IF_EXCEPTION(scope, void());
976
977     PropertyNameArray targetKeys(&vm, propertyNameMode, trapResult.privateSymbolMode());
978     target->methodTable(vm)->getOwnPropertyNames(target, exec, targetKeys, enumerationMode);
979     RETURN_IF_EXCEPTION(scope, void());
980     Vector<UniquedStringImpl*> targetConfigurableKeys;
981     Vector<UniquedStringImpl*> targetNonConfigurableKeys;
982     for (const Identifier& ident : targetKeys) {
983         PropertyDescriptor descriptor;
984         bool isPropertyDefined = target->getOwnPropertyDescriptor(exec, ident.impl(), descriptor); 
985         RETURN_IF_EXCEPTION(scope, void());
986         if (isPropertyDefined && !descriptor.configurable())
987             targetNonConfigurableKeys.append(ident.impl());
988         else
989             targetConfigurableKeys.append(ident.impl());
990     }
991
992     enum ContainedIn { IsContainedIn, IsNotContainedIn };
993     auto removeIfContainedInUncheckedResultKeys = [&] (UniquedStringImpl* impl) -> ContainedIn {
994         auto iter = uncheckedResultKeys.find(impl);
995         if (iter == uncheckedResultKeys.end())
996             return IsNotContainedIn;
997
998         uncheckedResultKeys.remove(iter);
999         return IsContainedIn;
1000     };
1001
1002     for (UniquedStringImpl* impl : targetNonConfigurableKeys) {
1003         if (removeIfContainedInUncheckedResultKeys(impl) == IsNotContainedIn) {
1004             throwVMTypeError(exec, scope, makeString("Proxy object's 'target' has the non-configurable property '", String(impl), "' that was not in the result from the 'ownKeys' trap"));
1005             return;
1006         }
1007     }
1008
1009     if (targetIsExensible)
1010         return;
1011
1012     for (UniquedStringImpl* impl : targetConfigurableKeys) {
1013         if (removeIfContainedInUncheckedResultKeys(impl) == IsNotContainedIn) {
1014             throwVMTypeError(exec, scope, makeString("Proxy object's non-extensible 'target' has configurable property '", String(impl), "' that was not in the result from the 'ownKeys' trap"));
1015             return;
1016         }
1017     }
1018
1019     if (uncheckedResultKeys.size()) {
1020         throwVMTypeError(exec, scope, "Proxy handler's 'ownKeys' method returned a key that was not present in its non-extensible target"_s);
1021         return;
1022     }
1023 }
1024
1025 void ProxyObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNameArray, EnumerationMode enumerationMode)
1026 {
1027     ProxyObject* thisObject = jsCast<ProxyObject*>(object);
1028     thisObject->performGetOwnPropertyNames(exec, propertyNameArray, enumerationMode);
1029 }
1030
1031 void ProxyObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNameArray, EnumerationMode enumerationMode)
1032 {
1033     NO_TAIL_CALLS();
1034     JSObject::getPropertyNames(object, exec, propertyNameArray, enumerationMode);
1035 }
1036
1037 void ProxyObject::getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode)
1038 {
1039     RELEASE_ASSERT_NOT_REACHED();
1040 }
1041
1042 void ProxyObject::getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode)
1043 {
1044     // We should always go down the getOwnPropertyNames path.
1045     RELEASE_ASSERT_NOT_REACHED();
1046 }
1047
1048 void ProxyObject::getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode)
1049 {
1050     RELEASE_ASSERT_NOT_REACHED();
1051 }
1052
1053 bool ProxyObject::performSetPrototype(ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1054 {
1055     NO_TAIL_CALLS();
1056
1057     ASSERT(prototype.isObject() || prototype.isNull());
1058
1059     VM& vm = exec->vm();
1060     auto scope = DECLARE_THROW_SCOPE(vm);
1061     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
1062         throwStackOverflowError(exec, scope);
1063         return false;
1064     }
1065
1066     JSValue handlerValue = this->handler();
1067     if (handlerValue.isNull()) {
1068         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
1069         return false;
1070     }
1071
1072     JSObject* handler = jsCast<JSObject*>(handlerValue);
1073     CallData callData;
1074     CallType callType;
1075     JSValue setPrototypeOfMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "setPrototypeOf"), "'setPrototypeOf' property of a Proxy's handler should be callable"_s);
1076     RETURN_IF_EXCEPTION(scope, false);
1077
1078     JSObject* target = this->target();
1079     if (setPrototypeOfMethod.isUndefined())
1080         RELEASE_AND_RETURN(scope, target->setPrototype(vm, exec, prototype, shouldThrowIfCantSet));
1081
1082     MarkedArgumentBuffer arguments;
1083     arguments.append(target);
1084     arguments.append(prototype);
1085     ASSERT(!arguments.hasOverflowed());
1086     JSValue trapResult = call(exec, setPrototypeOfMethod, callType, callData, handler, arguments);
1087     RETURN_IF_EXCEPTION(scope, false);
1088
1089     bool trapResultAsBool = trapResult.toBoolean(exec);
1090     RETURN_IF_EXCEPTION(scope, false);
1091     
1092     if (!trapResultAsBool) {
1093         if (shouldThrowIfCantSet)
1094             throwVMTypeError(exec, scope, "Proxy 'setPrototypeOf' returned false indicating it could not set the prototype value. The operation was expected to succeed"_s);
1095         return false;
1096     }
1097
1098     bool targetIsExtensible = target->isExtensible(exec);
1099     RETURN_IF_EXCEPTION(scope, false);
1100     if (targetIsExtensible)
1101         return true;
1102
1103     JSValue targetPrototype = target->getPrototype(vm, exec);
1104     RETURN_IF_EXCEPTION(scope, false);
1105     if (!sameValue(exec, prototype, targetPrototype)) {
1106         throwVMTypeError(exec, scope, "Proxy 'setPrototypeOf' trap returned true when its target is non-extensible and the new prototype value is not the same as the current prototype value. It should have returned false"_s);
1107         return false;
1108     }
1109
1110     return true;
1111 }
1112
1113 bool ProxyObject::setPrototype(JSObject* object, ExecState* exec, JSValue prototype, bool shouldThrowIfCantSet)
1114 {
1115     return jsCast<ProxyObject*>(object)->performSetPrototype(exec, prototype, shouldThrowIfCantSet);
1116 }
1117
1118 JSValue ProxyObject::performGetPrototype(ExecState* exec)
1119 {
1120     NO_TAIL_CALLS();
1121
1122     VM& vm = exec->vm();
1123     auto scope = DECLARE_THROW_SCOPE(vm);
1124     if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
1125         throwStackOverflowError(exec, scope);
1126         return { };
1127     }
1128
1129     JSValue handlerValue = this->handler();
1130     if (handlerValue.isNull()) {
1131         throwVMTypeError(exec, scope, s_proxyAlreadyRevokedErrorMessage);
1132         return { };
1133     }
1134
1135     JSObject* handler = jsCast<JSObject*>(handlerValue);
1136     CallData callData;
1137     CallType callType;
1138     JSValue getPrototypeOfMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "getPrototypeOf"), "'getPrototypeOf' property of a Proxy's handler should be callable"_s);
1139     RETURN_IF_EXCEPTION(scope, { });
1140
1141     JSObject* target = this->target();
1142     if (getPrototypeOfMethod.isUndefined()) 
1143         RELEASE_AND_RETURN(scope, target->getPrototype(vm, exec));
1144
1145     MarkedArgumentBuffer arguments;
1146     arguments.append(target);
1147     ASSERT(!arguments.hasOverflowed());
1148     JSValue trapResult = call(exec, getPrototypeOfMethod, callType, callData, handler, arguments);
1149     RETURN_IF_EXCEPTION(scope, { });
1150
1151     if (!trapResult.isObject() && !trapResult.isNull()) {
1152         throwVMTypeError(exec, scope, "Proxy handler's 'getPrototypeOf' trap should either return an object or null"_s);
1153         return { };
1154     }
1155
1156     bool targetIsExtensible = target->isExtensible(exec);
1157     RETURN_IF_EXCEPTION(scope, { });
1158     if (targetIsExtensible)
1159         return trapResult;
1160
1161     JSValue targetPrototype = target->getPrototype(vm, exec);
1162     RETURN_IF_EXCEPTION(scope, { });
1163     if (!sameValue(exec, targetPrototype, trapResult)) {
1164         throwVMTypeError(exec, scope, "Proxy's 'getPrototypeOf' trap for a non-extensible target should return the same value as the target's prototype"_s);
1165         return { };
1166     }
1167
1168     return trapResult;
1169 }
1170
1171 JSValue ProxyObject::getPrototype(JSObject* object, ExecState* exec)
1172 {
1173     return jsCast<ProxyObject*>(object)->performGetPrototype(exec);
1174 }
1175
1176 void ProxyObject::revoke(VM& vm)
1177
1178     // This should only ever be called once and we should strictly transition from Object to null.
1179     RELEASE_ASSERT(!m_handler.get().isNull() && m_handler.get().isObject());
1180     m_handler.set(vm, this, jsNull());
1181 }
1182
1183 bool ProxyObject::isRevoked() const
1184 {
1185     return handler().isNull();
1186 }
1187
1188 void ProxyObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
1189 {
1190     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
1191     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
1192     Base::visitChildren(thisObject, visitor);
1193
1194     visitor.append(thisObject->m_target);
1195     visitor.append(thisObject->m_handler);
1196 }
1197
1198 } // namespace JSC