LCOV - code coverage report
Current view: top level - js/src/proxy - ScriptedProxyHandler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 98 694 14.1 %
Date: 2017-07-14 16:53:18 Functions: 10 34 29.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "proxy/ScriptedProxyHandler.h"
       8             : 
       9             : #include "jsapi.h"
      10             : 
      11             : #include "vm/Interpreter.h" // For InstanceOfOperator
      12             : 
      13             : #include "jsobjinlines.h"
      14             : #include "vm/NativeObject-inl.h"
      15             : 
      16             : using namespace js;
      17             : 
      18             : using JS::IsArrayAnswer;
      19             : using mozilla::ArrayLength;
      20             : 
      21             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
      22             : // 9.1.6.2 IsCompatiblePropertyDescriptor.  BUT that method just calls
      23             : // 9.1.6.3 ValidateAndApplyPropertyDescriptor with two additional constant
      24             : // arguments.  Therefore step numbering is from the latter method, and
      25             : // resulting dead code has been removed.
      26             : static bool
      27           0 : IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDescriptor> desc,
      28             :                                Handle<PropertyDescriptor> current, bool* bp)
      29             : {
      30             :     // Step 2.
      31           0 :     if (!current.object()) {
      32             :         // Step 2a-b,e.  As |O| is always undefined, steps 2c-d fall away.
      33           0 :         *bp = extensible;
      34           0 :         return true;
      35             :     }
      36             : 
      37             :     // Step 3.
      38           0 :     if (!desc.hasValue() && !desc.hasWritable() &&
      39           0 :         !desc.hasGetterObject() && !desc.hasSetterObject() &&
      40           0 :         !desc.hasEnumerable() && !desc.hasConfigurable())
      41             :     {
      42           0 :         *bp = true;
      43           0 :         return true;
      44             :     }
      45             : 
      46             :     // Step 4.
      47           0 :     if ((!desc.hasWritable() ||
      48           0 :          (current.hasWritable() && desc.writable() == current.writable())) &&
      49           0 :         (!desc.hasGetterObject() || desc.getter() == current.getter()) &&
      50           0 :         (!desc.hasSetterObject() || desc.setter() == current.setter()) &&
      51           0 :         (!desc.hasEnumerable() || desc.enumerable() == current.enumerable()) &&
      52           0 :         (!desc.hasConfigurable() || desc.configurable() == current.configurable()))
      53             :     {
      54           0 :         if (!desc.hasValue()) {
      55           0 :             *bp = true;
      56           0 :             return true;
      57             :         }
      58           0 :         bool same = false;
      59           0 :         if (!SameValue(cx, desc.value(), current.value(), &same))
      60           0 :             return false;
      61           0 :         if (same) {
      62           0 :             *bp = true;
      63           0 :             return true;
      64             :         }
      65             :     }
      66             : 
      67             :     // Step 5.
      68           0 :     if (!current.configurable()) {
      69             :         // Step 5a.
      70           0 :         if (desc.hasConfigurable() && desc.configurable()) {
      71           0 :             *bp = false;
      72           0 :             return true;
      73             :         }
      74             : 
      75             :         // Step 5b.
      76           0 :         if (desc.hasEnumerable() && desc.enumerable() != current.enumerable()) {
      77           0 :             *bp = false;
      78           0 :             return true;
      79             :         }
      80             :     }
      81             : 
      82             :     // Step 6.
      83           0 :     if (desc.isGenericDescriptor()) {
      84           0 :         *bp = true;
      85           0 :         return true;
      86             :     }
      87             : 
      88             :     // Step 7.
      89           0 :     if (current.isDataDescriptor() != desc.isDataDescriptor()) {
      90             :         // Steps 7a, 11.  As |O| is always undefined, steps 2b-c fall away.
      91           0 :         *bp = current.configurable();
      92           0 :         return true;
      93             :     }
      94             : 
      95             :     // Step 8.
      96           0 :     if (current.isDataDescriptor()) {
      97           0 :         MOZ_ASSERT(desc.isDataDescriptor()); // by step 7
      98           0 :         if (!current.configurable() && !current.writable()) {
      99           0 :             if (desc.hasWritable() && desc.writable()) {
     100           0 :                 *bp = false;
     101           0 :                 return true;
     102             :             }
     103             : 
     104           0 :             if (desc.hasValue()) {
     105             :                 bool same;
     106           0 :                 if (!SameValue(cx, desc.value(), current.value(), &same))
     107           0 :                     return false;
     108           0 :                 if (!same) {
     109           0 :                     *bp = false;
     110           0 :                     return true;
     111             :                 }
     112             :             }
     113             :         }
     114             : 
     115           0 :         *bp = true;
     116           0 :         return true;
     117             :     }
     118             : 
     119             :     // Step 9.
     120           0 :     MOZ_ASSERT(current.isAccessorDescriptor()); // by step 8
     121           0 :     MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 7
     122           0 :     *bp = (current.configurable() ||
     123           0 :            ((!desc.hasSetterObject() || desc.setter() == current.setter()) &&
     124           0 :             (!desc.hasGetterObject() || desc.getter() == current.getter())));
     125           0 :     return true;
     126             : }
     127             : 
     128             : // Get the [[ProxyHandler]] of a scripted proxy.
     129             : /* static */ JSObject*
     130          71 : ScriptedProxyHandler::handlerObject(const JSObject* proxy)
     131             : {
     132          71 :     MOZ_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
     133          71 :     return proxy->as<ProxyObject>().reservedSlot(ScriptedProxyHandler::HANDLER_EXTRA).toObjectOrNull();
     134             : }
     135             : 
     136             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.9 GetMethod,
     137             : // reimplemented for proxy handler trap-getting to produce better error
     138             : // messages.
     139             : static bool
     140          71 : GetProxyTrap(JSContext* cx, HandleObject handler, HandlePropertyName name, MutableHandleValue func)
     141             : {
     142             :     // Steps 2, 5.
     143          71 :     if (!GetProperty(cx, handler, handler, name, func))
     144           0 :         return false;
     145             : 
     146             :     // Step 3.
     147          71 :     if (func.isUndefined())
     148          56 :         return true;
     149             : 
     150          15 :     if (func.isNull()) {
     151           0 :         func.setUndefined();
     152           0 :         return true;
     153             :     }
     154             : 
     155             :     // Step 4.
     156          15 :     if (!IsCallable(func)) {
     157           0 :         JSAutoByteString bytes(cx, name);
     158           0 :         if (!bytes)
     159           0 :             return false;
     160             : 
     161           0 :         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.ptr());
     162           0 :         return false;
     163             :     }
     164             : 
     165          15 :     return true;
     166             : }
     167             : 
     168             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.1 Proxy.[[GetPrototypeOf]].
     169             : bool
     170           0 : ScriptedProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
     171             :                                    MutableHandleObject protop) const
     172             : {
     173             :     // Steps 1-3.
     174           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     175           0 :     if (!handler) {
     176           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     177           0 :         return false;
     178             :     }
     179             : 
     180             :     // Step 4.
     181           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     182           0 :     MOZ_ASSERT(target);
     183             : 
     184             :     // Step 5.
     185           0 :     RootedValue trap(cx);
     186           0 :     if (!GetProxyTrap(cx, handler, cx->names().getPrototypeOf, &trap))
     187           0 :         return false;
     188             : 
     189             :     // Step 6.
     190           0 :     if (trap.isUndefined())
     191           0 :         return GetPrototype(cx, target, protop);
     192             : 
     193             :     // Step 7.
     194           0 :     RootedValue handlerProto(cx);
     195             :     {
     196           0 :         FixedInvokeArgs<1> args(cx);
     197             : 
     198           0 :         args[0].setObject(*target);
     199             : 
     200           0 :         handlerProto.setObject(*handler);
     201             : 
     202           0 :         if (!js::Call(cx, trap, handlerProto, args, &handlerProto))
     203           0 :             return false;
     204             :     }
     205             : 
     206             :     // Step 8.
     207           0 :     if (!handlerProto.isObjectOrNull()) {
     208             :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     209           0 :                                   JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN);
     210           0 :         return false;
     211             :     }
     212             : 
     213             :     // Step 9.
     214             :     bool extensibleTarget;
     215           0 :     if (!IsExtensible(cx, target, &extensibleTarget))
     216           0 :         return false;
     217             : 
     218             :     // Step 10.
     219           0 :     if (extensibleTarget) {
     220           0 :         protop.set(handlerProto.toObjectOrNull());
     221           0 :         return true;
     222             :     }
     223             : 
     224             :     // Step 11.
     225           0 :     RootedObject targetProto(cx);
     226           0 :     if (!GetPrototype(cx, target, &targetProto))
     227           0 :         return false;
     228             : 
     229             :     // Step 12.
     230           0 :     if (handlerProto.toObjectOrNull() != targetProto) {
     231             :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     232           0 :                                   JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP);
     233           0 :         return false;
     234             :     }
     235             : 
     236             :     // Step 13.
     237           0 :     protop.set(handlerProto.toObjectOrNull());
     238           0 :     return true;
     239             : }
     240             : 
     241             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.2 Proxy.[[SetPrototypeOf]].
     242             : bool
     243           0 : ScriptedProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
     244             :                                    ObjectOpResult& result) const
     245             : {
     246             :     // Steps 1-4.
     247           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     248           0 :     if (!handler) {
     249           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     250           0 :         return false;
     251             :     }
     252             : 
     253             :     // Step 5.
     254           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     255           0 :     MOZ_ASSERT(target);
     256             : 
     257             :     // Step 6.
     258           0 :     RootedValue trap(cx);
     259           0 :     if (!GetProxyTrap(cx, handler, cx->names().setPrototypeOf, &trap))
     260           0 :         return false;
     261             : 
     262             :     // Step 7.
     263           0 :     if (trap.isUndefined())
     264           0 :         return SetPrototype(cx, target, proto, result);
     265             : 
     266             :     // Step 8.
     267             :     bool booleanTrapResult;
     268             :     {
     269           0 :         FixedInvokeArgs<2> args(cx);
     270             : 
     271           0 :         args[0].setObject(*target);
     272           0 :         args[1].setObjectOrNull(proto);
     273             : 
     274           0 :         RootedValue hval(cx, ObjectValue(*handler));
     275           0 :         if (!js::Call(cx, trap, hval, args, &hval))
     276           0 :             return false;
     277             : 
     278           0 :         booleanTrapResult = ToBoolean(hval);
     279             :     }
     280             : 
     281             :     // Step 9.
     282           0 :     if (!booleanTrapResult)
     283           0 :         return result.fail(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE);
     284             : 
     285             :     // Step 10.
     286             :     bool extensibleTarget;
     287           0 :     if (!IsExtensible(cx, target, &extensibleTarget))
     288           0 :         return false;
     289             : 
     290             :     // Step 11.
     291           0 :     if (extensibleTarget)
     292           0 :         return result.succeed();
     293             : 
     294             :     // Step 12.
     295           0 :     RootedObject targetProto(cx);
     296           0 :     if (!GetPrototype(cx, target, &targetProto))
     297           0 :         return false;
     298             : 
     299             :     // Step 13.
     300           0 :     if (proto != targetProto) {
     301             :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     302           0 :                                   JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP);
     303           0 :         return false;
     304             :     }
     305             : 
     306             :     // Step 14.
     307           0 :     return result.succeed();
     308             : }
     309             : 
     310             : bool
     311           0 : ScriptedProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
     312             :                                              MutableHandleObject protop) const
     313             : {
     314           0 :     *isOrdinary = false;
     315           0 :     return true;
     316             : }
     317             : 
     318             : // Not yet part of ES6, but hopefully to be standards-tracked -- and needed to
     319             : // handle revoked proxies in any event.
     320             : bool
     321           0 : ScriptedProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy,
     322             :                                             bool* succeeded) const
     323             : {
     324           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     325           0 :     if (!target) {
     326           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     327           0 :         return false;
     328             :     }
     329             : 
     330           0 :     return SetImmutablePrototype(cx, target, succeeded);
     331             : }
     332             : 
     333             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.4 Proxy.[[PreventExtensions]]()
     334             : bool
     335           2 : ScriptedProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy,
     336             :                                         ObjectOpResult& result) const
     337             : {
     338             :     // Steps 1-3.
     339           4 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     340           2 :     if (!handler) {
     341           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     342           0 :         return false;
     343             :     }
     344             : 
     345             :     // Step 4.
     346           4 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     347           2 :     MOZ_ASSERT(target);
     348             : 
     349             :     // Step 5.
     350           4 :     RootedValue trap(cx);
     351           2 :     if (!GetProxyTrap(cx, handler, cx->names().preventExtensions, &trap))
     352           0 :         return false;
     353             : 
     354             :     // Step 6.
     355           2 :     if (trap.isUndefined())
     356           2 :         return PreventExtensions(cx, target, result);
     357             : 
     358             :     // Step 7.
     359             :     bool booleanTrapResult;
     360             :     {
     361           0 :         RootedValue arg(cx, ObjectValue(*target));
     362           0 :         RootedValue trapResult(cx);
     363           0 :         if (!Call(cx, trap, handler, arg, &trapResult))
     364           0 :             return false;
     365             : 
     366           0 :         booleanTrapResult = ToBoolean(trapResult);
     367             :     }
     368             : 
     369             :     // Step 8.
     370           0 :     if (booleanTrapResult) {
     371             :         // Step 8a.
     372             :         bool targetIsExtensible;
     373           0 :         if (!IsExtensible(cx, target, &targetIsExtensible))
     374           0 :             return false;
     375             : 
     376           0 :         if (targetIsExtensible) {
     377             :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     378           0 :                                       JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
     379           0 :             return false;
     380             :         }
     381             : 
     382             :         // Step 9.
     383           0 :         return result.succeed();
     384             :     }
     385             : 
     386             :     // Also step 9.
     387           0 :     return result.fail(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE);
     388             : }
     389             : 
     390             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.3 Proxy.[[IsExtensible]]()
     391             : bool
     392           0 : ScriptedProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
     393             : {
     394             :     // Steps 1-3.
     395           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     396           0 :     if (!handler) {
     397           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     398           0 :         return false;
     399             :     }
     400             : 
     401             :     // Step 4.
     402           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     403           0 :     MOZ_ASSERT(target);
     404             : 
     405             :     // Step 5.
     406           0 :     RootedValue trap(cx);
     407           0 :     if (!GetProxyTrap(cx, handler, cx->names().isExtensible, &trap))
     408           0 :         return false;
     409             : 
     410             :     // Step 6.
     411           0 :     if (trap.isUndefined())
     412           0 :         return IsExtensible(cx, target, extensible);
     413             : 
     414             :     // Step 7.
     415             :     bool booleanTrapResult;
     416             :     {
     417           0 :         RootedValue arg(cx, ObjectValue(*target));
     418           0 :         RootedValue trapResult(cx);
     419           0 :         if (!Call(cx, trap, handler, arg, &trapResult))
     420           0 :             return false;
     421             : 
     422           0 :         booleanTrapResult = ToBoolean(trapResult);
     423             :     }
     424             : 
     425             :     // Steps 8.
     426             :     bool targetResult;
     427           0 :     if (!IsExtensible(cx, target, &targetResult))
     428           0 :         return false;
     429             : 
     430             :     // Step 9.
     431           0 :     if (targetResult != booleanTrapResult) {
     432           0 :        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY);
     433           0 :        return false;
     434             :     }
     435             : 
     436             :     // Step 10.
     437           0 :     *extensible = booleanTrapResult;
     438           0 :     return true;
     439             : }
     440             : 
     441             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.5 Proxy.[[GetOwnProperty]](P)
     442             : bool
     443          26 : ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
     444             :                                                MutableHandle<PropertyDescriptor> desc) const
     445             : {
     446             :     // Steps 2-4.
     447          52 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     448          26 :     if (!handler) {
     449           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     450           0 :         return false;
     451             :     }
     452             : 
     453             :     // Step 5.
     454          52 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     455          26 :     MOZ_ASSERT(target);
     456             : 
     457             :     // Step 6.
     458          52 :     RootedValue trap(cx);
     459          26 :     if (!GetProxyTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &trap))
     460           0 :         return false;
     461             : 
     462             :     // Step 7.
     463          26 :     if (trap.isUndefined())
     464          26 :         return GetOwnPropertyDescriptor(cx, target, id, desc);
     465             : 
     466             :     // Step 8.
     467           0 :     RootedValue propKey(cx);
     468           0 :     if (!IdToStringOrSymbol(cx, id, &propKey))
     469           0 :         return false;
     470             : 
     471           0 :     RootedValue trapResult(cx);
     472           0 :     RootedValue targetVal(cx, ObjectValue(*target));
     473           0 :     if (!Call(cx, trap, handler, targetVal, propKey, &trapResult))
     474           0 :         return false;
     475             : 
     476             :     // Step 9.
     477           0 :     if (!trapResult.isUndefined() && !trapResult.isObject()) {
     478           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_GETOWN_OBJORUNDEF);
     479           0 :         return false;
     480             :     }
     481             : 
     482             :     // Step 10.
     483           0 :     Rooted<PropertyDescriptor> targetDesc(cx);
     484           0 :     if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc))
     485           0 :         return false;
     486             : 
     487             :     // Step 11.
     488           0 :     if (trapResult.isUndefined()) {
     489             :         // Step 11a.
     490           0 :         if (!targetDesc.object()) {
     491           0 :             desc.object().set(nullptr);
     492           0 :             return true;
     493             :         }
     494             : 
     495             :         // Step 11b.
     496           0 :         if (!targetDesc.configurable()) {
     497           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
     498           0 :             return false;
     499             :         }
     500             : 
     501             :         // Steps 11c-d.
     502             :         bool extensibleTarget;
     503           0 :         if (!IsExtensible(cx, target, &extensibleTarget))
     504           0 :             return false;
     505             : 
     506             :         // Step 11e.
     507           0 :         if (!extensibleTarget) {
     508           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
     509           0 :             return false;
     510             :         }
     511             : 
     512             :         // Step 11f.
     513           0 :         desc.object().set(nullptr);
     514           0 :         return true;
     515             :     }
     516             : 
     517             :     // Step 12.
     518             :     bool extensibleTarget;
     519           0 :     if (!IsExtensible(cx, target, &extensibleTarget))
     520           0 :         return false;
     521             : 
     522             :     // Step 13.
     523           0 :     Rooted<PropertyDescriptor> resultDesc(cx);
     524           0 :     if (!ToPropertyDescriptor(cx, trapResult, true, &resultDesc))
     525           0 :         return false;
     526             : 
     527             :     // Step 14.
     528           0 :     CompletePropertyDescriptor(&resultDesc);
     529             : 
     530             :     // Step 15.
     531             :     bool valid;
     532           0 :     if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid))
     533           0 :         return false;
     534             : 
     535             :     // Step 16.
     536           0 :     if (!valid) {
     537           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID);
     538           0 :         return false;
     539             :     }
     540             : 
     541             :     // Step 17.
     542           0 :     if (!resultDesc.configurable()) {
     543           0 :         if (!targetDesc.object()) {
     544           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC);
     545           0 :             return false;
     546             :         }
     547             : 
     548           0 :         if (targetDesc.configurable()) {
     549           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_C_AS_NC);
     550           0 :             return false;
     551             :         }
     552             :     }
     553             : 
     554             :     // Step 18.
     555           0 :     desc.set(resultDesc);
     556           0 :     desc.object().set(proxy);
     557           0 :     return true;
     558             : }
     559             : 
     560             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc)
     561             : bool
     562          26 : ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
     563             :                                      Handle<PropertyDescriptor> desc, ObjectOpResult& result) const
     564             : {
     565             :     // Steps 2-4.
     566          52 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     567          26 :     if (!handler) {
     568           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     569           0 :         return false;
     570             :     }
     571             : 
     572             :     // Step 5.
     573          52 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     574          26 :     MOZ_ASSERT(target);
     575             : 
     576             :     // Step 6.
     577          52 :     RootedValue trap(cx);
     578          26 :     if (!GetProxyTrap(cx, handler, cx->names().defineProperty, &trap))
     579           0 :         return false;
     580             : 
     581             :     // Step 7.
     582          26 :     if (trap.isUndefined())
     583          26 :         return DefineProperty(cx, target, id, desc, result);
     584             : 
     585             :     // Step 8.
     586           0 :     RootedValue descObj(cx);
     587           0 :     if (!FromPropertyDescriptorToObject(cx, desc, &descObj))
     588           0 :         return false;
     589             : 
     590             :     // Step 9.
     591           0 :     RootedValue propKey(cx);
     592           0 :     if (!IdToStringOrSymbol(cx, id, &propKey))
     593           0 :         return false;
     594             : 
     595           0 :     RootedValue trapResult(cx);
     596             :     {
     597           0 :         FixedInvokeArgs<3> args(cx);
     598             : 
     599           0 :         args[0].setObject(*target);
     600           0 :         args[1].set(propKey);
     601           0 :         args[2].set(descObj);
     602             : 
     603           0 :         RootedValue thisv(cx, ObjectValue(*handler));
     604           0 :         if (!Call(cx, trap, thisv, args, &trapResult))
     605           0 :             return false;
     606             :     }
     607             : 
     608             :     // Step 10.
     609           0 :     if (!ToBoolean(trapResult))
     610           0 :         return result.fail(JSMSG_PROXY_DEFINE_RETURNED_FALSE);
     611             : 
     612             :     // Step 11.
     613           0 :     Rooted<PropertyDescriptor> targetDesc(cx);
     614           0 :     if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc))
     615           0 :         return false;
     616             : 
     617             :     // Step 12.
     618             :     bool extensibleTarget;
     619           0 :     if (!IsExtensible(cx, target, &extensibleTarget))
     620           0 :         return false;
     621             : 
     622             :     // Steps 13-14.
     623           0 :     bool settingConfigFalse = desc.hasConfigurable() && !desc.configurable();
     624             : 
     625             :     // Steps 15-16.
     626           0 :     if (!targetDesc.object()) {
     627             :         // Step 15a.
     628           0 :         if (!extensibleTarget) {
     629           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
     630           0 :             return false;
     631             :         }
     632             : 
     633             :         // Step 15b.
     634           0 :         if (settingConfigFalse) {
     635           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
     636           0 :             return false;
     637             :         }
     638             :     } else {
     639             :         // Steps 16a-b.
     640             :         bool valid;
     641           0 :         if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc, &valid))
     642           0 :             return false;
     643             : 
     644           0 :         if (!valid || (settingConfigFalse && targetDesc.configurable())) {
     645           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
     646           0 :             return false;
     647             :         }
     648             :     }
     649             : 
     650             :     // Step 17.
     651           0 :     return result.succeed();
     652             : }
     653             : 
     654             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
     655             : // 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string.
     656             : static bool
     657           0 : CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& props)
     658             : {
     659             :     // Step 2.
     660           0 :     RootedObject obj(cx, NonNullObjectWithName(cx, "return value of the ownKeys trap", v));
     661           0 :     if (!obj)
     662           0 :         return false;
     663             : 
     664             :     // Step 3.
     665             :     uint32_t len;
     666           0 :     if (!GetLengthProperty(cx, obj, &len))
     667           0 :         return false;
     668             : 
     669             :     // Steps 4-6.
     670           0 :     RootedValue next(cx);
     671           0 :     RootedId id(cx);
     672           0 :     uint32_t index = 0;
     673           0 :     while (index < len) {
     674             :         // Steps 6a-b.
     675           0 :         if (!GetElement(cx, obj, obj, index, &next))
     676           0 :             return false;
     677             : 
     678             :         // Step 6c.
     679           0 :         if (!next.isString() && !next.isSymbol()) {
     680           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ONWKEYS_STR_SYM);
     681           0 :             return false;
     682             :         }
     683             : 
     684           0 :         if (!ValueToId<CanGC>(cx, next, &id))
     685           0 :             return false;
     686             : 
     687             :         // Step 6d.
     688           0 :         if (!props.append(id))
     689           0 :             return false;
     690             : 
     691             :         // Step 6e.
     692           0 :         index++;
     693             :     }
     694             : 
     695             :     // Step 7.
     696           0 :     return true;
     697             : }
     698             : 
     699             : 
     700             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.11 Proxy.[[OwnPropertyKeys]]()
     701             : bool
     702           2 : ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const
     703             : {
     704             :     // Steps 1-3.
     705           4 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     706           2 :     if (!handler) {
     707           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     708           0 :         return false;
     709             :     }
     710             : 
     711             :     // Step 4.
     712           4 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     713           2 :     MOZ_ASSERT(target);
     714             : 
     715             :     // Step 5.
     716           4 :     RootedValue trap(cx);
     717           2 :     if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap))
     718           0 :         return false;
     719             : 
     720             :     // Step 6.
     721           2 :     if (trap.isUndefined())
     722           2 :         return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props);
     723             : 
     724             :     // Step 7.
     725           0 :     RootedValue trapResultArray(cx);
     726           0 :     RootedValue targetVal(cx, ObjectValue(*target));
     727           0 :     if (!Call(cx, trap, handler, targetVal, &trapResultArray))
     728           0 :         return false;
     729             : 
     730             :     // Step 8.
     731           0 :     AutoIdVector trapResult(cx);
     732           0 :     if (!CreateFilteredListFromArrayLike(cx, trapResultArray, trapResult))
     733           0 :         return false;
     734             : 
     735             :     // Step 9.
     736             :     bool extensibleTarget;
     737           0 :     if (!IsExtensible(cx, target, &extensibleTarget))
     738           0 :         return false;
     739             : 
     740             :     // Steps 10-11.
     741           0 :     AutoIdVector targetKeys(cx);
     742           0 :     if (!GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &targetKeys))
     743           0 :         return false;
     744             : 
     745             :     // Steps 12-13.
     746           0 :     AutoIdVector targetConfigurableKeys(cx);
     747           0 :     AutoIdVector targetNonconfigurableKeys(cx);
     748             : 
     749             :     // Step 14.
     750           0 :     Rooted<PropertyDescriptor> desc(cx);
     751           0 :     for (size_t i = 0; i < targetKeys.length(); ++i) {
     752             :         // Step 14a.
     753           0 :         if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc))
     754           0 :             return false;
     755             : 
     756             :         // Steps 14b-c.
     757           0 :         if (desc.object() && !desc.configurable()) {
     758           0 :             if (!targetNonconfigurableKeys.append(targetKeys[i]))
     759           0 :                 return false;
     760             :         } else {
     761           0 :             if (!targetConfigurableKeys.append(targetKeys[i]))
     762           0 :                 return false;
     763             :         }
     764             :     }
     765             : 
     766             :     // Step 15.
     767           0 :     if (extensibleTarget && targetNonconfigurableKeys.empty())
     768           0 :         return props.appendAll(trapResult);
     769             : 
     770             :     // Step 16.
     771             :     // The algorithm below always removes all occurences of the same key
     772             :     // at once, so we can use a set here.
     773           0 :     Rooted<GCHashSet<jsid>> uncheckedResultKeys(cx, GCHashSet<jsid>(cx));
     774           0 :     if (!uncheckedResultKeys.init(trapResult.length()))
     775           0 :         return false;
     776             : 
     777           0 :     for (size_t i = 0, len = trapResult.length(); i < len; i++) {
     778           0 :         MOZ_ASSERT(!JSID_IS_VOID(trapResult[i]));
     779             : 
     780           0 :         if (!uncheckedResultKeys.put(trapResult[i]))
     781           0 :             return false;
     782             :     }
     783             : 
     784             :     // Step 17.
     785           0 :     for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) {
     786           0 :         MOZ_ASSERT(!JSID_IS_VOID(targetNonconfigurableKeys[i]));
     787             : 
     788           0 :         auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]);
     789             : 
     790             :         // Step 17a.
     791           0 :         if (!ptr) {
     792           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC);
     793           0 :             return false;
     794             :         }
     795             : 
     796             :         // Step 17b.
     797           0 :         uncheckedResultKeys.remove(ptr);
     798             :     }
     799             : 
     800             :     // Step 18.
     801           0 :     if (extensibleTarget)
     802           0 :         return props.appendAll(trapResult);
     803             : 
     804             :     // Step 19.
     805           0 :     for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) {
     806           0 :         MOZ_ASSERT(!JSID_IS_VOID(targetConfigurableKeys[i]));
     807             : 
     808           0 :         auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]);
     809             : 
     810             :         // Step 19a.
     811           0 :         if (!ptr) {
     812           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
     813           0 :             return false;
     814             :         }
     815             : 
     816             :         // Step 19b.
     817           0 :         uncheckedResultKeys.remove(ptr);
     818             :     }
     819             : 
     820             :     // Step 20.
     821           0 :     if (!uncheckedResultKeys.empty()) {
     822           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
     823           0 :         return false;
     824             :     }
     825             : 
     826             :     // Step 21.
     827           0 :     return props.appendAll(trapResult);
     828             : }
     829             : 
     830             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.10 Proxy.[[Delete]](P)
     831             : bool
     832           0 : ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
     833             :                               ObjectOpResult& result) const
     834             : {
     835             :     // Steps 2-4.
     836           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     837           0 :     if (!handler) {
     838           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     839           0 :         return false;
     840             :     }
     841             : 
     842             :     // Step 5.
     843           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     844           0 :     MOZ_ASSERT(target);
     845             : 
     846             :     // Step 6.
     847           0 :     RootedValue trap(cx);
     848           0 :     if (!GetProxyTrap(cx, handler, cx->names().deleteProperty, &trap))
     849           0 :         return false;
     850             : 
     851             :     // Step 7.
     852           0 :     if (trap.isUndefined())
     853           0 :         return DeleteProperty(cx, target, id, result);
     854             : 
     855             :     // Step 8.
     856             :     bool booleanTrapResult;
     857             :     {
     858           0 :         RootedValue value(cx);
     859           0 :         if (!IdToStringOrSymbol(cx, id, &value))
     860           0 :             return false;
     861             : 
     862           0 :         RootedValue targetVal(cx, ObjectValue(*target));
     863           0 :         RootedValue trapResult(cx);
     864           0 :         if (!Call(cx, trap, handler, targetVal, value, &trapResult))
     865           0 :             return false;
     866             : 
     867           0 :         booleanTrapResult = ToBoolean(trapResult);
     868             :     }
     869             : 
     870             :     // Step 9.
     871           0 :     if (!booleanTrapResult)
     872           0 :         return result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE);
     873             : 
     874             :     // Step 10.
     875           0 :     Rooted<PropertyDescriptor> desc(cx);
     876           0 :     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
     877           0 :         return false;
     878             : 
     879             :     // Step 12.
     880           0 :     if (desc.object() && !desc.configurable()) {
     881           0 :         RootedValue v(cx, IdToValue(id));
     882           0 :         ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, nullptr);
     883           0 :         return false;
     884             :     }
     885             : 
     886             :     // Steps 11,13.
     887           0 :     return result.succeed();
     888             : }
     889             : 
     890             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.7 Proxy.[[HasProperty]](P)
     891             : bool
     892           0 : ScriptedProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
     893             : {
     894             :     // Steps 2-4.
     895           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     896           0 :     if (!handler) {
     897           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     898           0 :         return false;
     899             :     }
     900             : 
     901             :     // Step 5.
     902           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     903           0 :     MOZ_ASSERT(target);
     904             : 
     905             :     // Step 6.
     906           0 :     RootedValue trap(cx);
     907           0 :     if (!GetProxyTrap(cx, handler, cx->names().has, &trap))
     908           0 :         return false;
     909             : 
     910             :     // Step 7.
     911           0 :     if (trap.isUndefined())
     912           0 :         return HasProperty(cx, target, id, bp);
     913             : 
     914             :     // Step 8.
     915           0 :     RootedValue value(cx);
     916           0 :     if (!IdToStringOrSymbol(cx, id, &value))
     917           0 :         return false;
     918             : 
     919           0 :     RootedValue trapResult(cx);
     920           0 :     RootedValue targetVal(cx, ObjectValue(*target));
     921           0 :     if (!Call(cx, trap, handler, targetVal, value, &trapResult))
     922           0 :         return false;
     923             : 
     924           0 :     bool booleanTrapResult = ToBoolean(trapResult);
     925             : 
     926             :     // Step 9.
     927           0 :     if (!booleanTrapResult) {
     928             :         // Step 9a.
     929           0 :         Rooted<PropertyDescriptor> desc(cx);
     930           0 :         if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
     931           0 :             return false;
     932             : 
     933             :         // Step 9b.
     934           0 :         if (desc.object()) {
     935             :             // Step 9b(i).
     936           0 :             if (!desc.configurable()) {
     937           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
     938           0 :                 return false;
     939             :             }
     940             : 
     941             :             // Step 9b(ii).
     942             :             bool extensible;
     943           0 :             if (!IsExtensible(cx, target, &extensible))
     944           0 :                 return false;
     945             : 
     946             :             // Step 9b(iii).
     947           0 :             if (!extensible) {
     948           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
     949           0 :                 return false;
     950             :             }
     951             :         }
     952             :     }
     953             : 
     954             :     // Step 10.
     955           0 :     *bp = booleanTrapResult;
     956           0 :     return true;
     957             : }
     958             : 
     959             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.8 Proxy.[[GetP]](P, Receiver)
     960             : bool
     961          15 : ScriptedProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
     962             :                           MutableHandleValue vp) const
     963             : {
     964             :     // Steps 2-4.
     965          30 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
     966          15 :     if (!handler) {
     967           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
     968           0 :         return false;
     969             :     }
     970             : 
     971             :     // Step 5.
     972          30 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
     973          15 :     MOZ_ASSERT(target);
     974             : 
     975             :     // Steps 6.
     976          30 :     RootedValue trap(cx);
     977          15 :     if (!GetProxyTrap(cx, handler, cx->names().get, &trap))
     978           0 :         return false;
     979             : 
     980             :     // Step 7.
     981          15 :     if (trap.isUndefined())
     982           0 :         return GetProperty(cx, target, receiver, id, vp);
     983             : 
     984             :     // Step 8.
     985          30 :     RootedValue value(cx);
     986          15 :     if (!IdToStringOrSymbol(cx, id, &value))
     987           0 :         return false;
     988             : 
     989          30 :     RootedValue trapResult(cx);
     990             :     {
     991          30 :         FixedInvokeArgs<3> args(cx);
     992             : 
     993          15 :         args[0].setObject(*target);
     994          15 :         args[1].set(value);
     995          15 :         args[2].set(receiver);
     996             : 
     997          30 :         RootedValue thisv(cx, ObjectValue(*handler));
     998          15 :         if (!Call(cx, trap, thisv, args, &trapResult))
     999           0 :             return false;
    1000             :     }
    1001             : 
    1002             :     // Step 9.
    1003          30 :     Rooted<PropertyDescriptor> desc(cx);
    1004          15 :     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
    1005           0 :         return false;
    1006             : 
    1007             :     // Step 10.
    1008          15 :     if (desc.object()) {
    1009             :         // Step 10a.
    1010           5 :         if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
    1011             :             bool same;
    1012           1 :             if (!SameValue(cx, trapResult, desc.value(), &same))
    1013           0 :                 return false;
    1014           1 :             if (!same) {
    1015           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE);
    1016           0 :                 return false;
    1017             :             }
    1018             :         }
    1019             : 
    1020             :         // Step 10b.
    1021           5 :         if (desc.isAccessorDescriptor() && !desc.configurable() && desc.getterObject() == nullptr) {
    1022           0 :             if (!trapResult.isUndefined()) {
    1023           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED);
    1024           0 :                 return false;
    1025             :             }
    1026             :         }
    1027             :     }
    1028             : 
    1029             :     // Step 11.
    1030          15 :     vp.set(trapResult);
    1031          15 :     return true;
    1032             : }
    1033             : 
    1034             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.9 Proxy.[[Set]](P, V, Receiver)
    1035             : bool
    1036           0 : ScriptedProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
    1037             :                           HandleValue receiver, ObjectOpResult& result) const
    1038             : {
    1039             :     // Steps 2-4.
    1040           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
    1041           0 :     if (!handler) {
    1042           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
    1043           0 :         return false;
    1044             :     }
    1045             : 
    1046             :     // Step 5.
    1047           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
    1048           0 :     MOZ_ASSERT(target);
    1049             : 
    1050             :     // Step 6.
    1051           0 :     RootedValue trap(cx);
    1052           0 :     if (!GetProxyTrap(cx, handler, cx->names().set, &trap))
    1053           0 :         return false;
    1054             : 
    1055             :     // Step 7.
    1056           0 :     if (trap.isUndefined())
    1057           0 :         return SetProperty(cx, target, id, v, receiver, result);
    1058             : 
    1059             :     // Step 8.
    1060           0 :     RootedValue value(cx);
    1061           0 :     if (!IdToStringOrSymbol(cx, id, &value))
    1062           0 :         return false;
    1063             : 
    1064           0 :     RootedValue trapResult(cx);
    1065             :     {
    1066           0 :         FixedInvokeArgs<4> args(cx);
    1067             : 
    1068           0 :         args[0].setObject(*target);
    1069           0 :         args[1].set(value);
    1070           0 :         args[2].set(v);
    1071           0 :         args[3].set(receiver);
    1072             : 
    1073           0 :         RootedValue thisv(cx, ObjectValue(*handler));
    1074           0 :         if (!Call(cx, trap, thisv, args, &trapResult))
    1075           0 :             return false;
    1076             :     }
    1077             : 
    1078             :     // Step 9.
    1079           0 :     if (!ToBoolean(trapResult))
    1080           0 :         return result.fail(JSMSG_PROXY_SET_RETURNED_FALSE);
    1081             : 
    1082             :     // Step 10.
    1083           0 :     Rooted<PropertyDescriptor> desc(cx);
    1084           0 :     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
    1085           0 :         return false;
    1086             : 
    1087             :     // Step 11.
    1088           0 :     if (desc.object()) {
    1089             :         // Step 11a.
    1090           0 :         if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) {
    1091             :             bool same;
    1092           0 :             if (!SameValue(cx, v, desc.value(), &same))
    1093           0 :                 return false;
    1094           0 :             if (!same) {
    1095           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC);
    1096           0 :                 return false;
    1097             :             }
    1098             :         }
    1099             : 
    1100             :         // Step 11b.
    1101           0 :         if (desc.isAccessorDescriptor() && !desc.configurable() && desc.setterObject() == nullptr) {
    1102           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER);
    1103           0 :             return false;
    1104             :         }
    1105             :     }
    1106             : 
    1107             :     // Step 12.
    1108           0 :     return result.succeed();
    1109             : }
    1110             : 
    1111             : // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.13 Proxy.[[Call]]
    1112             : bool
    1113           0 : ScriptedProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
    1114             : {
    1115             :     // Steps 1-3.
    1116           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
    1117           0 :     if (!handler) {
    1118           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
    1119           0 :         return false;
    1120             :     }
    1121             : 
    1122             :     // Step 4.
    1123           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
    1124           0 :     MOZ_ASSERT(target);
    1125           0 :     MOZ_ASSERT(target->isCallable());
    1126             : 
    1127             :     // Step 5.
    1128           0 :     RootedValue trap(cx);
    1129           0 :     if (!GetProxyTrap(cx, handler, cx->names().apply, &trap))
    1130           0 :         return false;
    1131             : 
    1132             :     // Step 6.
    1133           0 :     if (trap.isUndefined()) {
    1134           0 :         InvokeArgs iargs(cx);
    1135           0 :         if (!FillArgumentsFromArraylike(cx, iargs, args))
    1136           0 :             return false;
    1137             : 
    1138           0 :         RootedValue fval(cx, ObjectValue(*target));
    1139           0 :         return js::Call(cx, fval, args.thisv(), iargs, args.rval());
    1140             :     }
    1141             : 
    1142             :     // Step 7.
    1143           0 :     RootedObject argArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
    1144           0 :     if (!argArray)
    1145           0 :         return false;
    1146             : 
    1147             :     // Step 8.
    1148           0 :     FixedInvokeArgs<3> iargs(cx);
    1149             : 
    1150           0 :     iargs[0].setObject(*target);
    1151           0 :     iargs[1].set(args.thisv());
    1152           0 :     iargs[2].setObject(*argArray);
    1153             : 
    1154           0 :     RootedValue thisv(cx, ObjectValue(*handler));
    1155           0 :     return js::Call(cx, trap, thisv, iargs, args.rval());
    1156             : }
    1157             : 
    1158             : // ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 Proxy.[[Construct]]
    1159             : bool
    1160           0 : ScriptedProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
    1161             : {
    1162             :     // Steps 1-3.
    1163           0 :     RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy));
    1164           0 :     if (!handler) {
    1165           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
    1166           0 :         return false;
    1167             :     }
    1168             : 
    1169             :     // Step 4.
    1170           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
    1171           0 :     MOZ_ASSERT(target);
    1172           0 :     MOZ_ASSERT(target->isConstructor());
    1173             : 
    1174             :     // Step 5.
    1175           0 :     RootedValue trap(cx);
    1176           0 :     if (!GetProxyTrap(cx, handler, cx->names().construct, &trap))
    1177           0 :         return false;
    1178             : 
    1179             :     // Step 6.
    1180           0 :     if (trap.isUndefined()) {
    1181           0 :         ConstructArgs cargs(cx);
    1182           0 :         if (!FillArgumentsFromArraylike(cx, cargs, args))
    1183           0 :             return false;
    1184             : 
    1185           0 :         RootedValue targetv(cx, ObjectValue(*target));
    1186           0 :         RootedObject obj(cx);
    1187           0 :         if (!Construct(cx, targetv, cargs, args.newTarget(), &obj))
    1188           0 :             return false;
    1189             : 
    1190           0 :         args.rval().setObject(*obj);
    1191           0 :         return true;
    1192             :     }
    1193             : 
    1194             :     // Step 7.
    1195           0 :     RootedObject argArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
    1196           0 :     if (!argArray)
    1197           0 :         return false;
    1198             : 
    1199             :     // Steps 8, 10.
    1200             :     {
    1201           0 :         FixedInvokeArgs<3> iargs(cx);
    1202             : 
    1203           0 :         iargs[0].setObject(*target);
    1204           0 :         iargs[1].setObject(*argArray);
    1205           0 :         iargs[2].set(args.newTarget());
    1206             : 
    1207           0 :         RootedValue thisv(cx, ObjectValue(*handler));
    1208           0 :         if (!Call(cx, trap, thisv, iargs, args.rval()))
    1209           0 :             return false;
    1210             :     }
    1211             : 
    1212             :     // Step 9.
    1213           0 :     if (!args.rval().isObject()) {
    1214           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT);
    1215           0 :         return false;
    1216             :     }
    1217             : 
    1218           0 :     return true;
    1219             : }
    1220             : 
    1221             : bool
    1222           0 : ScriptedProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
    1223             :                                  const CallArgs& args) const
    1224             : {
    1225           0 :     ReportIncompatible(cx, args);
    1226           0 :     return false;
    1227             : }
    1228             : 
    1229             : bool
    1230           0 : ScriptedProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
    1231             :                                   bool* bp) const
    1232             : {
    1233           0 :     return InstanceOfOperator(cx, proxy, v, bp);
    1234             : }
    1235             : 
    1236             : bool
    1237           0 : ScriptedProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
    1238             :                                       ESClass* cls) const
    1239             : {
    1240           0 :     *cls = ESClass::Other;
    1241           0 :     return true;
    1242             : }
    1243             : 
    1244             : bool
    1245           0 : ScriptedProxyHandler::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer) const
    1246             : {
    1247           0 :     RootedObject target(cx, proxy->as<ProxyObject>().target());
    1248           0 :     if (target)
    1249           0 :         return JS::IsArray(cx, target, answer);
    1250             : 
    1251           0 :     *answer = IsArrayAnswer::RevokedProxy;
    1252           0 :     return true;
    1253             : }
    1254             : 
    1255             : const char*
    1256           0 : ScriptedProxyHandler::className(JSContext* cx, HandleObject proxy) const
    1257             : {
    1258             :     // Right now the caller is not prepared to handle failures.
    1259           0 :     return BaseProxyHandler::className(cx, proxy);
    1260             : }
    1261             : 
    1262             : JSString*
    1263           0 : ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
    1264             : {
    1265             :     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
    1266           0 :                               js_Function_str, js_toString_str, "object");
    1267           0 :     return nullptr;
    1268             : }
    1269             : 
    1270             : RegExpShared*
    1271           0 : ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const
    1272             : {
    1273           0 :     MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
    1274             : }
    1275             : 
    1276             : bool
    1277           0 : ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
    1278             :                                        MutableHandleValue vp) const
    1279             : {
    1280           0 :     MOZ_CRASH("Should not end up in ScriptedProxyHandler::boxedValue_unbox");
    1281             :     return false;
    1282             : }
    1283             : 
    1284             : bool
    1285           0 : ScriptedProxyHandler::isCallable(JSObject* obj) const
    1286             : {
    1287           0 :     MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
    1288           0 :     uint32_t callConstruct = obj->as<ProxyObject>().reservedSlot(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
    1289           0 :     return !!(callConstruct & IS_CALLABLE);
    1290             : }
    1291             : 
    1292             : bool
    1293           0 : ScriptedProxyHandler::isConstructor(JSObject* obj) const
    1294             : {
    1295           0 :     MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
    1296           0 :     uint32_t callConstruct = obj->as<ProxyObject>().reservedSlot(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
    1297           0 :     return !!(callConstruct & IS_CONSTRUCTOR);
    1298             : }
    1299             : 
    1300             : const char ScriptedProxyHandler::family = 0;
    1301             : const ScriptedProxyHandler ScriptedProxyHandler::singleton;
    1302             : 
    1303             : bool
    1304           8 : IsRevokedScriptedProxy(JSObject* obj)
    1305             : {
    1306           8 :     obj = CheckedUnwrap(obj);
    1307           8 :     return obj && IsScriptedProxy(obj) && !obj->as<ProxyObject>().target();
    1308             : }
    1309             : 
    1310             : // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 ProxyCreate.
    1311             : static bool
    1312           4 : ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName)
    1313             : {
    1314           4 :     if (args.length() < 2) {
    1315             :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
    1316           0 :                                   callerName, "1", "s");
    1317           0 :         return false;
    1318             :     }
    1319             : 
    1320             :     // Step 1.
    1321           8 :     RootedObject target(cx, NonNullObjectArg(cx, "`target`", callerName, args[0]));
    1322           4 :     if (!target)
    1323           0 :         return false;
    1324             : 
    1325             :     // Step 2.
    1326           4 :     if (IsRevokedScriptedProxy(target)) {
    1327           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_ARG_REVOKED, "1");
    1328           0 :         return false;
    1329             :     }
    1330             : 
    1331             :     // Step 3.
    1332           8 :     RootedObject handler(cx, NonNullObjectArg(cx, "`handler`", callerName, args[1]));
    1333           4 :     if (!handler)
    1334           0 :         return false;
    1335             : 
    1336             :     // Step 4.
    1337           4 :     if (IsRevokedScriptedProxy(handler)) {
    1338           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_ARG_REVOKED, "2");
    1339           0 :         return false;
    1340             :     }
    1341             : 
    1342             :     // Steps 5-6, 8.
    1343           8 :     RootedValue priv(cx, ObjectValue(*target));
    1344             :     JSObject* proxy_ =
    1345           4 :         NewProxyObject(cx, &ScriptedProxyHandler::singleton, priv, TaggedProto::LazyProto);
    1346           4 :     if (!proxy_)
    1347           0 :         return false;
    1348             : 
    1349             :     // Step 9 (reordered).
    1350           8 :     Rooted<ProxyObject*> proxy(cx, &proxy_->as<ProxyObject>());
    1351           4 :     proxy->setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, ObjectValue(*handler));
    1352             : 
    1353             :     // Step 7.
    1354           4 :     uint32_t callable = target->isCallable() ? ScriptedProxyHandler::IS_CALLABLE : 0;
    1355           4 :     uint32_t constructor = target->isConstructor() ? ScriptedProxyHandler::IS_CONSTRUCTOR : 0;
    1356           4 :     proxy->setReservedSlot(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
    1357           8 :                            PrivateUint32Value(callable | constructor));
    1358             : 
    1359             :     // Step 10.
    1360           4 :     args.rval().setObject(*proxy);
    1361           4 :     return true;
    1362             : }
    1363             : 
    1364             : bool
    1365           4 : js::proxy(JSContext* cx, unsigned argc, Value* vp)
    1366             : {
    1367           4 :     CallArgs args = CallArgsFromVp(argc, vp);
    1368             : 
    1369           4 :     if (!ThrowIfNotConstructing(cx, args, "Proxy"))
    1370           0 :         return false;
    1371             : 
    1372           4 :     return ProxyCreate(cx, args, "Proxy");
    1373             : }
    1374             : 
    1375             : static bool
    1376           0 : RevokeProxy(JSContext* cx, unsigned argc, Value* vp)
    1377             : {
    1378           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1379             : 
    1380           0 :     RootedFunction func(cx, &args.callee().as<JSFunction>());
    1381           0 :     RootedObject p(cx, func->getExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT).toObjectOrNull());
    1382             : 
    1383           0 :     if (p) {
    1384           0 :         func->setExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, NullValue());
    1385             : 
    1386           0 :         MOZ_ASSERT(p->is<ProxyObject>());
    1387             : 
    1388           0 :         p->as<ProxyObject>().setSameCompartmentPrivate(NullValue());
    1389           0 :         p->as<ProxyObject>().setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, NullValue());
    1390             :     }
    1391             : 
    1392           0 :     args.rval().setUndefined();
    1393           0 :     return true;
    1394             : }
    1395             : 
    1396             : bool
    1397           0 : js::proxy_revocable(JSContext* cx, unsigned argc, Value* vp)
    1398             : {
    1399           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1400             : 
    1401           0 :     if (!ProxyCreate(cx, args, "Proxy.revocable"))
    1402           0 :         return false;
    1403             : 
    1404           0 :     RootedValue proxyVal(cx, args.rval());
    1405           0 :     MOZ_ASSERT(proxyVal.toObject().is<ProxyObject>());
    1406             : 
    1407           0 :     RootedObject revoker(cx, NewFunctionByIdWithReserved(cx, RevokeProxy, 0, 0,
    1408           0 :                          AtomToId(cx->names().revoke)));
    1409           0 :     if (!revoker)
    1410           0 :         return false;
    1411             : 
    1412           0 :     revoker->as<JSFunction>().initExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, proxyVal);
    1413             : 
    1414           0 :     RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx));
    1415           0 :     if (!result)
    1416           0 :         return false;
    1417             : 
    1418           0 :     RootedValue revokeVal(cx, ObjectValue(*revoker));
    1419           0 :     if (!DefineProperty(cx, result, cx->names().proxy, proxyVal) ||
    1420           0 :         !DefineProperty(cx, result, cx->names().revoke, revokeVal))
    1421             :     {
    1422           0 :         return false;
    1423             :     }
    1424             : 
    1425           0 :     args.rval().setObject(*result);
    1426           0 :     return true;
    1427             : }

Generated by: LCOV version 1.13