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