LCOV - code coverage report
Current view: top level - js/src/proxy - Proxy.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 255 429 59.4 %
Date: 2017-07-14 16:53:18 Functions: 39 53 73.6 %
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 "js/Proxy.h"
       8             : 
       9             : #include "mozilla/Attributes.h"
      10             : 
      11             : #include <string.h>
      12             : 
      13             : #include "jsapi.h"
      14             : #include "jscntxt.h"
      15             : #include "jsfun.h"
      16             : #include "jsgc.h"
      17             : #include "jswrapper.h"
      18             : 
      19             : #include "gc/Marking.h"
      20             : #include "proxy/DeadObjectProxy.h"
      21             : #include "proxy/ScriptedProxyHandler.h"
      22             : #include "vm/WrapperObject.h"
      23             : 
      24             : #include "jsatominlines.h"
      25             : #include "jsobjinlines.h"
      26             : 
      27             : #include "vm/NativeObject-inl.h"
      28             : 
      29             : using namespace js;
      30             : using namespace js::gc;
      31             : 
      32             : void
      33           0 : js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id)
      34             : {
      35           0 :     if (JS_IsExceptionPending(cx))
      36           0 :         return;
      37             : 
      38           0 :     if (JSID_IS_VOID(id)) {
      39           0 :         ReportAccessDenied(cx);
      40             :     } else {
      41           0 :         RootedValue idVal(cx, IdToValue(id));
      42           0 :         JSString* str = ValueToSource(cx, idVal);
      43           0 :         if (!str) {
      44           0 :             return;
      45             :         }
      46           0 :         AutoStableStringChars chars(cx);
      47           0 :         const char16_t* prop = nullptr;
      48           0 :         if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
      49           0 :             prop = chars.twoByteChars();
      50             : 
      51             :         JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
      52           0 :                                prop);
      53             :     }
      54             : }
      55             : 
      56             : #ifdef DEBUG
      57             : void
      58       19339 : js::AutoEnterPolicy::recordEnter(JSContext* cx, HandleObject proxy, HandleId id, Action act)
      59             : {
      60       19339 :     if (allowed()) {
      61       19339 :         context = cx;
      62       19339 :         enteredProxy.emplace(proxy);
      63       19339 :         enteredId.emplace(id);
      64       19339 :         enteredAction = act;
      65       19339 :         prev = cx->enteredPolicy;
      66       19339 :         cx->enteredPolicy = this;
      67             :     }
      68       19339 : }
      69             : 
      70             : void
      71       19339 : js::AutoEnterPolicy::recordLeave()
      72             : {
      73       19339 :     if (enteredProxy) {
      74       19339 :         MOZ_ASSERT(context->enteredPolicy == this);
      75       19339 :         context->enteredPolicy = prev;
      76             :     }
      77       19339 : }
      78             : 
      79             : JS_FRIEND_API(void)
      80       19896 : js::assertEnteredPolicy(JSContext* cx, JSObject* proxy, jsid id,
      81             :                         BaseProxyHandler::Action act)
      82             : {
      83       19896 :     MOZ_ASSERT(proxy->is<ProxyObject>());
      84       19896 :     MOZ_ASSERT(cx->enteredPolicy);
      85       19896 :     MOZ_ASSERT(cx->enteredPolicy->enteredProxy->get() == proxy);
      86       19896 :     MOZ_ASSERT(cx->enteredPolicy->enteredId->get() == id);
      87       19896 :     MOZ_ASSERT(cx->enteredPolicy->enteredAction & act);
      88       19896 : }
      89             : #endif
      90             : 
      91             : bool
      92           0 : Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
      93             :                              MutableHandle<PropertyDescriptor> desc)
      94             : {
      95           0 :     if (!CheckRecursionLimit(cx))
      96           0 :         return false;
      97             : 
      98           0 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
      99           0 :     desc.object().set(nullptr); // default result if we refuse to perform this action
     100           0 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
     101           0 :     if (!policy.allowed())
     102           0 :         return policy.returnValue();
     103             : 
     104             :     // Special case. See the comment on BaseProxyHandler::mHasPrototype.
     105           0 :     if (handler->hasPrototype())
     106           0 :         return handler->BaseProxyHandler::getPropertyDescriptor(cx, proxy, id, desc);
     107             : 
     108           0 :     return handler->getPropertyDescriptor(cx, proxy, id, desc);
     109             : }
     110             : 
     111             : bool
     112         153 : Proxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
     113             :                                 MutableHandle<PropertyDescriptor> desc)
     114             : {
     115         153 :     if (!CheckRecursionLimit(cx))
     116           0 :         return false;
     117         153 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     118         153 :     desc.object().set(nullptr); // default result if we refuse to perform this action
     119         306 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
     120         153 :     if (!policy.allowed())
     121           0 :         return policy.returnValue();
     122         153 :     return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
     123             : }
     124             : 
     125             : bool
     126        2029 : Proxy::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
     127             :                       Handle<PropertyDescriptor> desc, ObjectOpResult& result)
     128             : {
     129        2029 :     if (!CheckRecursionLimit(cx))
     130           0 :         return false;
     131        2029 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     132        4058 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     133        2029 :     if (!policy.allowed()) {
     134           0 :         if (!policy.returnValue())
     135           0 :             return false;
     136           0 :         return result.succeed();
     137             :     }
     138        2029 :     return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc, result);
     139             : }
     140             : 
     141             : bool
     142          10 : Proxy::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
     143             : {
     144          10 :     if (!CheckRecursionLimit(cx))
     145           0 :         return false;
     146          10 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     147          20 :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
     148          10 :     if (!policy.allowed())
     149           0 :         return policy.returnValue();
     150          10 :     return proxy->as<ProxyObject>().handler()->ownPropertyKeys(cx, proxy, props);
     151             : }
     152             : 
     153             : bool
     154         414 : Proxy::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
     155             : {
     156         414 :     if (!CheckRecursionLimit(cx))
     157           0 :         return false;
     158         414 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     159         828 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     160         414 :     if (!policy.allowed()) {
     161           0 :         bool ok = policy.returnValue();
     162           0 :         if (ok)
     163           0 :             result.succeed();
     164           0 :         return ok;
     165             :     }
     166         414 :     return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, result);
     167             : }
     168             : 
     169             : JS_FRIEND_API(bool)
     170           0 : js::AppendUnique(JSContext* cx, AutoIdVector& base, AutoIdVector& others)
     171             : {
     172           0 :     AutoIdVector uniqueOthers(cx);
     173           0 :     if (!uniqueOthers.reserve(others.length()))
     174           0 :         return false;
     175           0 :     for (size_t i = 0; i < others.length(); ++i) {
     176           0 :         bool unique = true;
     177           0 :         for (size_t j = 0; j < base.length(); ++j) {
     178           0 :             if (others[i].get() == base[j]) {
     179           0 :                 unique = false;
     180           0 :                 break;
     181             :             }
     182             :         }
     183           0 :         if (unique) {
     184           0 :             if (!uniqueOthers.append(others[i]))
     185           0 :                 return false;
     186             :         }
     187             :     }
     188           0 :     return base.appendAll(uniqueOthers);
     189             : }
     190             : 
     191             : /* static */ bool
     192         440 : Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto)
     193             : {
     194         440 :     MOZ_ASSERT(proxy->hasDynamicPrototype());
     195         440 :     if (!CheckRecursionLimit(cx))
     196           0 :         return false;
     197         440 :     return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto);
     198             : }
     199             : 
     200             : /* static */ bool
     201           0 : Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result)
     202             : {
     203           0 :     MOZ_ASSERT(proxy->hasDynamicPrototype());
     204           0 :     if (!CheckRecursionLimit(cx))
     205           0 :         return false;
     206           0 :     return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto, result);
     207             : }
     208             : 
     209             : /* static */ bool
     210           5 : Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
     211             :                               MutableHandleObject proto)
     212             : {
     213           5 :     if (!CheckRecursionLimit(cx))
     214           0 :         return false;
     215           5 :     return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary,
     216           5 :                                                                       proto);
     217             : }
     218             : 
     219             : /* static */ bool
     220           0 : Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded)
     221             : {
     222           0 :     if (!CheckRecursionLimit(cx))
     223           0 :         return false;
     224           0 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     225           0 :     return handler->setImmutablePrototype(cx, proxy, succeeded);
     226             : }
     227             : 
     228             : /* static */ bool
     229           3 : Proxy::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
     230             : {
     231           3 :     if (!CheckRecursionLimit(cx))
     232           0 :         return false;
     233           3 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     234           3 :     return handler->preventExtensions(cx, proxy, result);
     235             : }
     236             : 
     237             : /* static */ bool
     238           0 : Proxy::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
     239             : {
     240           0 :     if (!CheckRecursionLimit(cx))
     241           0 :         return false;
     242           0 :     return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy, extensible);
     243             : }
     244             : 
     245             : bool
     246         854 : Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
     247             : {
     248         854 :     if (!CheckRecursionLimit(cx))
     249           0 :         return false;
     250         854 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     251         854 :     *bp = false; // default result if we refuse to perform this action
     252        1708 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     253         854 :     if (!policy.allowed())
     254           0 :         return policy.returnValue();
     255             : 
     256         854 :     if (handler->hasPrototype()) {
     257         496 :         if (!handler->hasOwn(cx, proxy, id, bp))
     258           0 :             return false;
     259         496 :         if (*bp)
     260          53 :             return true;
     261             : 
     262         886 :         RootedObject proto(cx);
     263         443 :         if (!GetPrototype(cx, proxy, &proto))
     264           0 :             return false;
     265         443 :         if (!proto)
     266          41 :             return true;
     267             : 
     268         402 :         return HasProperty(cx, proto, id, bp);
     269             :     }
     270             : 
     271         358 :     return handler->has(cx, proxy, id, bp);
     272             : }
     273             : 
     274             : bool
     275          52 : Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
     276             : {
     277          52 :     if (!CheckRecursionLimit(cx))
     278           0 :         return false;
     279          52 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     280          52 :     *bp = false; // default result if we refuse to perform this action
     281         104 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     282          52 :     if (!policy.allowed())
     283           0 :         return policy.returnValue();
     284          52 :     return handler->hasOwn(cx, proxy, id, bp);
     285             : }
     286             : 
     287             : bool
     288           0 : js::ProxyHasOwn(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue result)
     289             : {
     290           0 :     RootedId id(cx);
     291           0 :     if (!ValueToId<CanGC>(cx, idVal, &id))
     292           0 :         return false;
     293             : 
     294             :     bool hasOwn;
     295           0 :     if (!Proxy::hasOwn(cx, proxy, id, &hasOwn))
     296           0 :         return false;
     297             : 
     298           0 :     result.setBoolean(hasOwn);
     299           0 :     return true;
     300             : }
     301             : 
     302             : static MOZ_ALWAYS_INLINE Value
     303       10606 : ValueToWindowProxyIfWindow(const Value& v)
     304             : {
     305       10606 :     if (v.isObject())
     306       10606 :         return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
     307           0 :     return v;
     308             : }
     309             : 
     310             : MOZ_ALWAYS_INLINE bool
     311       10202 : Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_, HandleId id,
     312             :            MutableHandleValue vp)
     313             : {
     314       10202 :     if (!CheckRecursionLimit(cx))
     315           0 :         return false;
     316       10202 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     317       10202 :     vp.setUndefined(); // default result if we refuse to perform this action
     318       20404 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     319       10202 :     if (!policy.allowed())
     320           0 :         return policy.returnValue();
     321             : 
     322             :     // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
     323             :     // shouldn't have to know about the Window/WindowProxy distinction.
     324       20404 :     RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
     325             : 
     326       10202 :     if (handler->hasPrototype()) {
     327             :         bool own;
     328         449 :         if (!handler->hasOwn(cx, proxy, id, &own))
     329         237 :             return false;
     330         449 :         if (!own) {
     331         474 :             RootedObject proto(cx);
     332         237 :             if (!GetPrototype(cx, proxy, &proto))
     333           0 :                 return false;
     334         237 :             if (!proto)
     335           2 :                 return true;
     336         235 :             return GetProperty(cx, proto, receiver, id, vp);
     337             :         }
     338             :     }
     339             : 
     340        9965 :     return handler->get(cx, proxy, receiver, id, vp);
     341             : }
     342             : 
     343             : bool
     344        1099 : js::ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
     345             : {
     346        2198 :     RootedValue receiver(cx, ObjectValue(*proxy));
     347        2198 :     return Proxy::get(cx, proxy, receiver, id, vp);
     348             : }
     349             : 
     350             : bool
     351         798 : js::ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal,
     352             :                             MutableHandleValue vp)
     353             : {
     354        1596 :     RootedId id(cx);
     355         798 :     if (!ValueToId<CanGC>(cx, idVal, &id))
     356           0 :         return false;
     357             : 
     358        1596 :     RootedValue receiver(cx, ObjectValue(*proxy));
     359         798 :     return Proxy::get(cx, proxy, receiver, id, vp);
     360             : }
     361             : 
     362             : bool
     363         404 : Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver_,
     364             :            ObjectOpResult& result)
     365             : {
     366         404 :     if (!CheckRecursionLimit(cx))
     367           0 :         return false;
     368         404 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     369         808 :     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     370         404 :     if (!policy.allowed()) {
     371           0 :         if (!policy.returnValue())
     372           0 :             return false;
     373           0 :         return result.succeed();
     374             :     }
     375             : 
     376             :     // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
     377             :     // shouldn't have to know about the Window/WindowProxy distinction.
     378         808 :     RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
     379             : 
     380             :     // Special case. See the comment on BaseProxyHandler::mHasPrototype.
     381         404 :     if (handler->hasPrototype())
     382          28 :         return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
     383             : 
     384         376 :     return handler->set(cx, proxy, id, v, receiver, result);
     385             : }
     386             : 
     387             : bool
     388          24 : js::ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, HandleValue val, bool strict)
     389             : {
     390          24 :     ObjectOpResult result;
     391          48 :     RootedValue receiver(cx, ObjectValue(*proxy));
     392          24 :     if (!Proxy::set(cx, proxy, id, val, receiver, result))
     393           0 :         return false;
     394          24 :     return result.checkStrictErrorOrWarning(cx, proxy, id, strict);
     395             : }
     396             : 
     397             : bool
     398          97 : js::ProxySetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal, HandleValue val,
     399             :                             bool strict)
     400             : {
     401         194 :     RootedId id(cx);
     402          97 :     if (!ValueToId<CanGC>(cx, idVal, &id))
     403           0 :         return false;
     404             : 
     405          97 :     ObjectOpResult result;
     406         194 :     RootedValue receiver(cx, ObjectValue(*proxy));
     407          97 :     if (!Proxy::set(cx, proxy, id, val, receiver, result))
     408           0 :         return false;
     409          97 :     return result.checkStrictErrorOrWarning(cx, proxy, id, strict);
     410             : }
     411             : 
     412             : bool
     413          49 : Proxy::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
     414             : {
     415          49 :     if (!CheckRecursionLimit(cx))
     416           0 :         return false;
     417          49 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     418          98 :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
     419          49 :     if (!policy.allowed())
     420           0 :         return policy.returnValue();
     421          49 :     return handler->getOwnEnumerablePropertyKeys(cx, proxy, props);
     422             : }
     423             : 
     424             : JSObject*
     425           5 : Proxy::enumerate(JSContext* cx, HandleObject proxy)
     426             : {
     427           5 :     if (!CheckRecursionLimit(cx))
     428           0 :         return nullptr;
     429             : 
     430           5 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     431           5 :     if (handler->hasPrototype()) {
     432           0 :         AutoIdVector props(cx);
     433           0 :         if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
     434           0 :             return nullptr;
     435             : 
     436           0 :         RootedObject proto(cx);
     437           0 :         if (!GetPrototype(cx, proxy, &proto))
     438           0 :             return nullptr;
     439           0 :         if (!proto)
     440           0 :             return EnumeratedIdVectorToIterator(cx, proxy, 0, props);
     441           0 :         assertSameCompartment(cx, proxy, proto);
     442             : 
     443           0 :         AutoIdVector protoProps(cx);
     444           0 :         if (!GetPropertyKeys(cx, proto, 0, &protoProps))
     445           0 :             return nullptr;
     446           0 :         if (!AppendUnique(cx, props, protoProps))
     447           0 :             return nullptr;
     448           0 :         return EnumeratedIdVectorToIterator(cx, proxy, 0, props);
     449             :     }
     450             : 
     451             :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
     452          10 :                            BaseProxyHandler::ENUMERATE, true);
     453             : 
     454             :     // If the policy denies access but wants us to return true, we need
     455             :     // to hand a valid (empty) iterator object to the caller.
     456           5 :     if (!policy.allowed()) {
     457           0 :         if (!policy.returnValue())
     458           0 :             return nullptr;
     459           0 :         return NewEmptyPropertyIterator(cx, 0);
     460             :     }
     461           5 :     return handler->enumerate(cx, proxy);
     462             : }
     463             : 
     464             : bool
     465        4925 : Proxy::call(JSContext* cx, HandleObject proxy, const CallArgs& args)
     466             : {
     467        4925 :     if (!CheckRecursionLimit(cx))
     468           0 :         return false;
     469        4925 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     470             : 
     471             :     // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
     472             :     // can only set our default value once we're sure that we're not calling the
     473             :     // trap.
     474             :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
     475        9850 :                            BaseProxyHandler::CALL, true);
     476        4925 :     if (!policy.allowed()) {
     477           0 :         args.rval().setUndefined();
     478           0 :         return policy.returnValue();
     479             :     }
     480             : 
     481        4925 :     return handler->call(cx, proxy, args);
     482             : }
     483             : 
     484             : bool
     485         176 : Proxy::construct(JSContext* cx, HandleObject proxy, const CallArgs& args)
     486             : {
     487         176 :     if (!CheckRecursionLimit(cx))
     488           0 :         return false;
     489         176 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     490             : 
     491             :     // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
     492             :     // can only set our default value once we're sure that we're not calling the
     493             :     // trap.
     494             :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
     495         352 :                            BaseProxyHandler::CALL, true);
     496         176 :     if (!policy.allowed()) {
     497           0 :         args.rval().setUndefined();
     498           0 :         return policy.returnValue();
     499             :     }
     500             : 
     501         176 :     return handler->construct(cx, proxy, args);
     502             : }
     503             : 
     504             : bool
     505           0 : Proxy::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args)
     506             : {
     507           0 :     if (!CheckRecursionLimit(cx))
     508           0 :         return false;
     509           0 :     RootedObject proxy(cx, &args.thisv().toObject());
     510             :     // Note - we don't enter a policy here because our security architecture
     511             :     // guards against nativeCall by overriding the trap itself in the right
     512             :     // circumstances.
     513           0 :     return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
     514             : }
     515             : 
     516             : bool
     517          24 : Proxy::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
     518             : {
     519          24 :     if (!CheckRecursionLimit(cx))
     520           0 :         return false;
     521          24 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     522          24 :     *bp = false; // default result if we refuse to perform this action
     523          48 :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
     524          24 :     if (!policy.allowed())
     525           0 :         return policy.returnValue();
     526          24 :     return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
     527             : }
     528             : 
     529             : bool
     530         115 : Proxy::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
     531             : {
     532         115 :     if (!CheckRecursionLimit(cx))
     533           0 :         return false;
     534         115 :     return proxy->as<ProxyObject>().handler()->getBuiltinClass(cx, proxy, cls);
     535             : }
     536             : 
     537             : bool
     538          55 : Proxy::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer)
     539             : {
     540          55 :     if (!CheckRecursionLimit(cx))
     541           0 :         return false;
     542          55 :     return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
     543             : }
     544             : 
     545             : const char*
     546           0 : Proxy::className(JSContext* cx, HandleObject proxy)
     547             : {
     548             :     // Check for unbounded recursion, but don't signal an error; className
     549             :     // needs to be infallible.
     550             :     int stackDummy;
     551           0 :     if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
     552           0 :         return "too much recursion";
     553             : 
     554           0 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     555             :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
     556           0 :                            BaseProxyHandler::GET, /* mayThrow = */ false);
     557             :     // Do the safe thing if the policy rejects.
     558           0 :     if (!policy.allowed()) {
     559           0 :         return handler->BaseProxyHandler::className(cx, proxy);
     560             :     }
     561           0 :     return handler->className(cx, proxy);
     562             : }
     563             : 
     564             : JSString*
     565           0 : Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
     566             : {
     567           0 :     if (!CheckRecursionLimit(cx))
     568           0 :         return nullptr;
     569           0 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     570             :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
     571           0 :                            BaseProxyHandler::GET, /* mayThrow = */ false);
     572             :     // Do the safe thing if the policy rejects.
     573           0 :     if (!policy.allowed())
     574           0 :         return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
     575           0 :     return handler->fun_toString(cx, proxy, indent);
     576             : }
     577             : 
     578             : RegExpShared*
     579           0 : Proxy::regexp_toShared(JSContext* cx, HandleObject proxy)
     580             : {
     581           0 :     if (!CheckRecursionLimit(cx))
     582           0 :         return nullptr;
     583           0 :     return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy);
     584             : }
     585             : 
     586             : bool
     587           0 : Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp)
     588             : {
     589           0 :     if (!CheckRecursionLimit(cx))
     590           0 :         return false;
     591           0 :     return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
     592             : }
     593             : 
     594             : JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
     595             : 
     596             : /* static */ bool
     597           0 : Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
     598             : {
     599           0 :     if (!CheckRecursionLimit(cx))
     600           0 :         return false;
     601           0 :     return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
     602             : }
     603             : 
     604             : /* static */ bool
     605           0 : Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id)
     606             : {
     607           0 :     if (!CheckRecursionLimit(cx))
     608           0 :         return false;
     609           0 :     return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
     610             : }
     611             : 
     612             : /* static */ bool
     613          35 : Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
     614             :                    ElementAdder* adder)
     615             : {
     616          35 :     if (!CheckRecursionLimit(cx))
     617           0 :         return false;
     618          35 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     619             :     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
     620          70 :                            /* mayThrow = */ true);
     621          35 :     if (!policy.allowed()) {
     622           0 :         if (policy.returnValue()) {
     623           0 :             MOZ_ASSERT(!cx->isExceptionPending());
     624           0 :             return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
     625             :         }
     626           0 :         return false;
     627             :     }
     628          35 :     return handler->getElements(cx, proxy, begin, end, adder);
     629             : }
     630             : 
     631             : /* static */ void
     632        2112 : Proxy::trace(JSTracer* trc, JSObject* proxy)
     633             : {
     634        2112 :     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     635        2112 :     handler->trace(trc, proxy);
     636        2112 : }
     637             : 
     638             : static bool
     639         397 : proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
     640             :                      MutableHandleObject objp, MutableHandle<JS::PropertyResult> propp)
     641             : {
     642             :     bool found;
     643         397 :     if (!Proxy::has(cx, obj, id, &found))
     644           0 :         return false;
     645             : 
     646         397 :     if (found) {
     647          51 :         propp.setNonNativeProperty();
     648          51 :         objp.set(obj);
     649             :     } else {
     650         346 :         propp.setNotFound();
     651         346 :         objp.set(nullptr);
     652             :     }
     653         397 :     return true;
     654             : }
     655             : 
     656             : static bool
     657         414 : proxy_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
     658             : {
     659         414 :     if (!Proxy::delete_(cx, obj, id, result))
     660           0 :         return false;
     661         414 :     return SuppressDeletedProperty(cx, obj, id); // XXX is this necessary?
     662             : }
     663             : 
     664             : /* static */ void
     665        2112 : ProxyObject::trace(JSTracer* trc, JSObject* obj)
     666             : {
     667        2112 :     ProxyObject* proxy = &obj->as<ProxyObject>();
     668             : 
     669        2112 :     TraceEdge(trc, &proxy->shape_, "ProxyObject_shape");
     670             : 
     671             : #ifdef DEBUG
     672        2112 :     if (TlsContext.get()->isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
     673           8 :         JSObject* referent = MaybeForwarded(proxy->target());
     674           8 :         if (referent->compartment() != proxy->compartment()) {
     675             :             /*
     676             :              * Assert that this proxy is tracked in the wrapper map. We maintain
     677             :              * the invariant that the wrapped object is the key in the wrapper map.
     678             :              */
     679           8 :             Value key = ObjectValue(*referent);
     680           8 :             WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
     681           8 :             MOZ_ASSERT(p);
     682           8 :             MOZ_ASSERT(*p->value().unsafeGet() == ObjectValue(*proxy));
     683             :         }
     684             :     }
     685             : #endif
     686             : 
     687             :     // Note: If you add new slots here, make sure to change
     688             :     // nuke() to cope.
     689        2112 :     TraceCrossCompartmentEdge(trc, obj, proxy->slotOfPrivate(), "private");
     690             : 
     691        2112 :     size_t nreserved = proxy->numReservedSlots();
     692        6335 :     for (size_t i = 0; i < nreserved; i++) {
     693             :         /*
     694             :          * The GC can use the second reserved slot to link the cross compartment
     695             :          * wrappers into a linked list, in which case we don't want to trace it.
     696             :          */
     697        4223 :         if (proxy->is<CrossCompartmentWrapperObject>() &&
     698             :             i == CrossCompartmentWrapperObject::GrayLinkReservedSlot)
     699             :         {
     700        2110 :             continue;
     701             :         }
     702        2113 :         TraceEdge(trc, proxy->reservedSlotPtr(i), "proxy_reserved");
     703             :     }
     704             : 
     705        2112 :     Proxy::trace(trc, obj);
     706        2112 : }
     707             : 
     708             : JSObject*
     709          12 : js::proxy_WeakmapKeyDelegate(JSObject* obj)
     710             : {
     711          12 :     MOZ_ASSERT(obj->is<ProxyObject>());
     712          12 :     return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
     713             : }
     714             : 
     715             : static void
     716          11 : proxy_Finalize(FreeOp* fop, JSObject* obj)
     717             : {
     718             :     // Suppress a bogus warning about finalize().
     719          22 :     JS::AutoSuppressGCAnalysis nogc;
     720             : 
     721          11 :     MOZ_ASSERT(obj->is<ProxyObject>());
     722          11 :     obj->as<ProxyObject>().handler()->finalize(fop, obj);
     723             : 
     724          11 :     if (!obj->as<ProxyObject>().usingInlineValueArray())
     725           0 :         js_free(js::detail::GetProxyDataLayout(obj)->values());
     726          11 : }
     727             : 
     728             : static void
     729        2101 : proxy_ObjectMoved(JSObject* obj, const JSObject* old)
     730             : {
     731        2101 :     MOZ_ASSERT(obj->is<ProxyObject>());
     732        2101 :     obj->as<ProxyObject>().handler()->objectMoved(obj, old);
     733        2101 : }
     734             : 
     735             : bool
     736        4925 : js::proxy_Call(JSContext* cx, unsigned argc, Value* vp)
     737             : {
     738        4925 :     CallArgs args = CallArgsFromVp(argc, vp);
     739        9850 :     RootedObject proxy(cx, &args.callee());
     740        4925 :     MOZ_ASSERT(proxy->is<ProxyObject>());
     741        9850 :     return Proxy::call(cx, proxy, args);
     742             : }
     743             : 
     744             : bool
     745         176 : js::proxy_Construct(JSContext* cx, unsigned argc, Value* vp)
     746             : {
     747         176 :     CallArgs args = CallArgsFromVp(argc, vp);
     748         352 :     RootedObject proxy(cx, &args.callee());
     749         176 :     MOZ_ASSERT(proxy->is<ProxyObject>());
     750         352 :     return Proxy::construct(cx, proxy, args);
     751             : }
     752             : 
     753             : const ClassOps js::ProxyClassOps = {
     754             :     nullptr,                 /* addProperty */
     755             :     nullptr,                 /* delProperty */
     756             :     nullptr,                 /* getProperty */
     757             :     nullptr,                 /* setProperty */
     758             :     nullptr,                 /* enumerate   */
     759             :     nullptr,                 /* newEnumerate */
     760             :     nullptr,                 /* resolve     */
     761             :     nullptr,                 /* mayResolve  */
     762             :     proxy_Finalize,          /* finalize    */
     763             :     nullptr,                 /* call        */
     764             :     Proxy::hasInstance,      /* hasInstance */
     765             :     nullptr,                 /* construct   */
     766             :     ProxyObject::trace,      /* trace       */
     767             : };
     768             : 
     769             : const ClassExtension js::ProxyClassExtension = PROXY_MAKE_EXT(
     770             :     proxy_ObjectMoved
     771             : );
     772             : 
     773             : const ObjectOps js::ProxyObjectOps = {
     774             :     proxy_LookupProperty,
     775             :     Proxy::defineProperty,
     776             :     Proxy::has,
     777             :     Proxy::get,
     778             :     Proxy::set,
     779             :     Proxy::getOwnPropertyDescriptor,
     780             :     proxy_DeleteProperty,
     781             :     Proxy::watch, Proxy::unwatch,
     782             :     Proxy::getElements,
     783             :     Proxy::fun_toString
     784             : };
     785             : 
     786             : const Class js::ProxyObject::proxyClass =
     787             :     PROXY_CLASS_DEF("Proxy",
     788             :                     JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) |
     789             :                     JSCLASS_HAS_RESERVED_SLOTS(2));
     790             : 
     791             : const Class* const js::ProxyClassPtr = &js::ProxyObject::proxyClass;
     792             : 
     793             : JS_FRIEND_API(JSObject*)
     794        9333 : js::NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, JSObject* proto_,
     795             :                    const ProxyOptions& options)
     796             : {
     797        9333 :     if (options.lazyProto()) {
     798           5 :         MOZ_ASSERT(!proto_);
     799           5 :         proto_ = TaggedProto::LazyProto;
     800             :     }
     801             : 
     802        9333 :     return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
     803             : }
     804             : 
     805             : void
     806          10 : ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv)
     807             : {
     808          10 :     MOZ_ASSERT(!IsInsideNursery(this));
     809          10 :     MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
     810          10 :     MOZ_ASSERT(getClass() == &ProxyObject::proxyClass);
     811          10 :     MOZ_ASSERT(!IsWindowProxy(this));
     812          10 :     MOZ_ASSERT(hasDynamicPrototype());
     813             : 
     814          10 :     setHandler(handler);
     815          10 :     setCrossCompartmentPrivate(priv);
     816          30 :     for (size_t i = 0; i < numReservedSlots(); i++)
     817          20 :         setReservedSlot(i, UndefinedValue());
     818          10 : }
     819             : 
     820             : JS_FRIEND_API(JSObject*)
     821           9 : js::InitProxyClass(JSContext* cx, HandleObject obj)
     822             : {
     823             :     static const JSFunctionSpec static_methods[] = {
     824             :         JS_FN("revocable",      proxy_revocable,       2, 0),
     825             :         JS_FS_END
     826             :     };
     827             : 
     828           9 :     Handle<GlobalObject*> global = obj.as<GlobalObject>();
     829          18 :     RootedFunction ctor(cx);
     830           9 :     ctor = GlobalObject::createConstructor(cx, proxy, cx->names().Proxy, 2);
     831           9 :     if (!ctor)
     832           0 :         return nullptr;
     833             : 
     834           9 :     if (!JS_DefineFunctions(cx, ctor, static_methods))
     835           0 :         return nullptr;
     836           9 :     if (!JS_DefineProperty(cx, obj, "Proxy", ctor, JSPROP_RESOLVING, JS_STUBGETTER, JS_STUBSETTER))
     837           0 :         return nullptr;
     838             : 
     839           9 :     global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
     840           9 :     return ctor;
     841             : }

Generated by: LCOV version 1.13