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