js/xpconnect/src/XPCQuickStubs.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     7 #ifndef xpcquickstubs_h___
     8 #define xpcquickstubs_h___
    10 #include "XPCForwards.h"
    12 class qsObjectHelper;
    13 namespace mozilla {
    14 namespace dom {
    15 class NativePropertiesHolder;
    16 }
    17 }
    19 /* XPCQuickStubs.h - Support functions used only by quick stubs. */
    21 class XPCCallContext;
    23 #define XPC_QS_NULL_INDEX  ((uint16_t) -1)
    25 struct xpc_qsPropertySpec {
    26     uint16_t name_index;
    27     JSNative getter;
    28     JSNative setter;
    29 };
    31 struct xpc_qsFunctionSpec {
    32     uint16_t name_index;
    33     uint16_t arity;
    34     JSNative native;
    35 };
    37 /** A table mapping interfaces to quick stubs. */
    38 struct xpc_qsHashEntry {
    39     nsID iid;
    40     uint16_t prop_index;
    41     uint16_t n_props;
    42     uint16_t func_index;
    43     uint16_t n_funcs;
    44     const mozilla::dom::NativePropertiesHolder* newBindingProperties;
    45     // These last two fields index to other entries in the same table.
    46     // XPC_QS_NULL_ENTRY indicates there are no more entries in the chain.
    47     uint16_t parentInterface;
    48     uint16_t chain;
    49 };
    51 bool
    52 xpc_qsDefineQuickStubs(JSContext *cx, JSObject *proto, unsigned extraFlags,
    53                        uint32_t ifacec, const nsIID **interfaces,
    54                        uint32_t tableSize, const xpc_qsHashEntry *table,
    55                        const xpc_qsPropertySpec *propspecs,
    56                        const xpc_qsFunctionSpec *funcspecs,
    57                        const char *stringTable);
    59 /** Raise an exception on @a cx and return false. */
    60 bool
    61 xpc_qsThrow(JSContext *cx, nsresult rv);
    63 /**
    64  * Fail after an XPCOM getter or setter returned rv.
    65  *
    66  * NOTE: Here @a obj must be the JSObject whose private data field points to an
    67  * XPCWrappedNative, not merely an object that has an XPCWrappedNative
    68  * somewhere along the prototype chain!  The same applies to @a obj in
    69  * xpc_qsThrowBadSetterValue and <code>vp[1]</code> in xpc_qsThrowMethodFailed
    70  * and xpc_qsThrowBadArg.
    71  *
    72  * This is one reason the UnwrapThis functions below have an out parameter that
    73  * receives the wrapper JSObject.  (The other reason is to help the caller keep
    74  * that JSObject GC-reachable.)
    75  */
    76 bool
    77 xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv,
    78                               JSObject *obj, jsid memberId);
    79 // And variants using strings and string tables
    80 bool
    81 xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv,
    82                               JSObject *obj, const char* memberName);
    83 bool
    84 xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv,
    85                               JSObject *obj, uint16_t memberIndex);
    87 /**
    88  * Fail after an XPCOM method returned rv.
    89  *
    90  * See NOTE at xpc_qsThrowGetterSetterFailed.
    91  */
    92 bool
    93 xpc_qsThrowMethodFailed(JSContext *cx, nsresult rv, jsval *vp);
    95 /**
    96  * Fail after converting a method argument fails.
    97  *
    98  * See NOTE at xpc_qsThrowGetterSetterFailed.
    99  */
   100 void
   101 xpc_qsThrowBadArg(JSContext *cx, nsresult rv, jsval *vp, unsigned paramnum);
   103 void
   104 xpc_qsThrowBadArgWithCcx(XPCCallContext &ccx, nsresult rv, unsigned paramnum);
   106 void
   107 xpc_qsThrowBadArgWithDetails(JSContext *cx, nsresult rv, unsigned paramnum,
   108                              const char *ifaceName, const char *memberName);
   110 /**
   111  * Fail after converting a setter argument fails.
   112  *
   113  * See NOTE at xpc_qsThrowGetterSetterFailed.
   114  */
   115 void
   116 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
   117                           jsid propId);
   118 // And variants using strings and string tables
   119 void
   120 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
   121                           const char* propName);
   122 void
   123 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
   124                           uint16_t name_index);
   127 bool
   128 xpc_qsGetterOnlyPropertyStub(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
   129                              bool strict, JS::MutableHandleValue vp);
   131 bool
   132 xpc_qsGetterOnlyNativeStub(JSContext *cx, unsigned argc, jsval *vp);
   134 /* Functions for converting values between COM and JS. */
   136 inline bool
   137 xpc_qsInt64ToJsval(JSContext *cx, int64_t i, JS::MutableHandleValue rv)
   138 {
   139     rv.setNumber(static_cast<double>(i));
   140     return true;
   141 }
   143 inline bool
   144 xpc_qsUint64ToJsval(JSContext *cx, uint64_t u, JS::MutableHandleValue rv)
   145 {
   146     rv.setNumber(static_cast<double>(u));
   147     return true;
   148 }
   151 /* Classes for converting jsvals to string types. */
   153 template <class S, class T>
   154 class xpc_qsBasicString
   155 {
   156 public:
   157     typedef S interface_type;
   158     typedef T implementation_type;
   160     ~xpc_qsBasicString()
   161     {
   162         if (mValid)
   163             Ptr()->~implementation_type();
   164     }
   166     bool IsValid() const { return mValid; }
   168     implementation_type *Ptr()
   169     {
   170         MOZ_ASSERT(mValid);
   171         return reinterpret_cast<implementation_type *>(mBuf);
   172     }
   174     const implementation_type *Ptr() const
   175     {
   176         MOZ_ASSERT(mValid);
   177         return reinterpret_cast<const implementation_type *>(mBuf);
   178     }
   180     operator interface_type &()
   181     {
   182         MOZ_ASSERT(mValid);
   183         return *Ptr();
   184     }
   186     operator const interface_type &() const
   187     {
   188         MOZ_ASSERT(mValid);
   189         return *Ptr();
   190     }
   192     /* Enum that defines how JS |null| and |undefined| should be treated.  See
   193      * the WebIDL specification.  eStringify means convert to the string "null"
   194      * or "undefined" respectively, via the standard JS ToString() operation;
   195      * eEmpty means convert to the string ""; eNull means convert to an empty
   196      * string with the void bit set.
   197      *
   198      * Per webidl the default behavior of an unannotated interface is
   199      * eStringify, but our de-facto behavior has been eNull for |null| and
   200      * eStringify for |undefined|, so leaving it that way for now.  If we ever
   201      * get to a point where we go through and annotate our interfaces as
   202      * needed, we can change that.
   203      */
   204     enum StringificationBehavior {
   205         eStringify,
   206         eEmpty,
   207         eNull,
   208         eDefaultNullBehavior = eNull,
   209         eDefaultUndefinedBehavior = eStringify
   210     };
   212 protected:
   213     /*
   214      * Neither field is initialized; that is left to the derived class
   215      * constructor. However, the destructor destroys the string object
   216      * stored in mBuf, if mValid is true.
   217      */
   218     void *mBuf[JS_HOWMANY(sizeof(implementation_type), sizeof(void *))];
   219     bool mValid;
   221     /*
   222      * If null is returned, then we either failed or fully initialized
   223      * |this|; in either case the caller should return immediately
   224      * without doing anything else. Otherwise, the JSString* created
   225      * from |v| will be returned.  It'll be rooted, as needed, in
   226      * *pval.  nullBehavior and undefinedBehavior control what happens
   227      * when |v| is JSVAL_IS_NULL and JSVAL_IS_VOID respectively.
   228      */
   229     template<class traits>
   230     JSString* InitOrStringify(JSContext* cx, JS::HandleValue v,
   231                               JS::MutableHandleValue pval,
   232                               bool notpassed,
   233                               StringificationBehavior nullBehavior,
   234                               StringificationBehavior undefinedBehavior) {
   235         JSString *s;
   236         if (JSVAL_IS_STRING(v)) {
   237             s = JSVAL_TO_STRING(v);
   238         } else {
   239             StringificationBehavior behavior = eStringify;
   240             if (JSVAL_IS_NULL(v)) {
   241                 behavior = nullBehavior;
   242             } else if (JSVAL_IS_VOID(v)) {
   243                 behavior = undefinedBehavior;
   244             }
   246             // If pval is null, that means the argument was optional and
   247             // not passed; turn those into void strings if they're
   248             // supposed to be stringified.
   249             if (behavior != eStringify || notpassed) {
   250                 // Here behavior == eStringify implies notpassed, so both eNull and
   251                 // eStringify should end up with void strings.
   252                 (new(mBuf) implementation_type(traits::sEmptyBuffer, uint32_t(0)))->
   253                     SetIsVoid(behavior != eEmpty);
   254                 mValid = true;
   255                 return nullptr;
   256             }
   258             s = JS::ToString(cx, v);
   259             if (!s) {
   260                 mValid = false;
   261                 return nullptr;
   262             }
   263             pval.setString(s);  // Root the new string.
   264         }
   266         return s;
   267     }
   268 };
   270 /**
   271  * Class for converting a jsval to DOMString.
   272  *
   273  *     xpc_qsDOMString arg0(cx, &argv[0]);
   274  *     if (!arg0.IsValid())
   275  *         return false;
   276  *
   277  * The second argument to the constructor is an in-out parameter. It must
   278  * point to a rooted jsval, such as a JSNative argument or return value slot.
   279  * The value in the jsval on entry is converted to a string. The constructor
   280  * may overwrite that jsval with a string value, to protect the characters of
   281  * the string from garbage collection. The caller must leave the jsval alone
   282  * for the lifetime of the xpc_qsDOMString.
   283  */
   284 class xpc_qsDOMString : public xpc_qsBasicString<nsAString, nsDependentString>
   285 {
   286 public:
   287     xpc_qsDOMString(JSContext *cx, JS::HandleValue v,
   288                     JS::MutableHandleValue pval, bool notpassed,
   289                     StringificationBehavior nullBehavior,
   290                     StringificationBehavior undefinedBehavior);
   291 };
   293 /**
   294  * The same as xpc_qsDOMString, but with slightly different conversion behavior,
   295  * corresponding to the [astring] magic XPIDL annotation rather than [domstring].
   296  */
   297 class xpc_qsAString : public xpc_qsDOMString
   298 {
   299 public:
   300     xpc_qsAString(JSContext *cx, JS::HandleValue v,
   301                   JS::MutableHandleValue pval, bool notpassed)
   302         : xpc_qsDOMString(cx, v, pval, notpassed, eNull, eNull)
   303     {}
   304 };
   306 /**
   307  * Like xpc_qsDOMString and xpc_qsAString, but for XPIDL native types annotated
   308  * with [cstring] rather than [domstring] or [astring].
   309  */
   310 class xpc_qsACString : public xpc_qsBasicString<nsACString, nsCString>
   311 {
   312 public:
   313     xpc_qsACString(JSContext *cx, JS::HandleValue v,
   314                    JS::MutableHandleValue pval, bool notpassed,
   315                    StringificationBehavior nullBehavior = eNull,
   316                    StringificationBehavior undefinedBehavior = eNull);
   317 };
   319 /**
   320  * And similar for AUTF8String.
   321  */
   322 class xpc_qsAUTF8String :
   323   public xpc_qsBasicString<nsACString, NS_ConvertUTF16toUTF8>
   324 {
   325 public:
   326   xpc_qsAUTF8String(JSContext* cx, JS::HandleValue v,
   327                     JS::MutableHandleValue pval, bool notpassed);
   328 };
   330 struct xpc_qsSelfRef
   331 {
   332     xpc_qsSelfRef() : ptr(nullptr) {}
   333     explicit xpc_qsSelfRef(nsISupports *p) : ptr(p) {}
   334     ~xpc_qsSelfRef() { NS_IF_RELEASE(ptr); }
   336     nsISupports* ptr;
   337 };
   339 /**
   340  * Convert a jsval to char*, returning true on success.
   341  *
   342  * @param cx
   343  *     A context.
   344  * @param v
   345  *     A value to convert.
   346  * @param bytes
   347  *     Out. On success it receives the converted string unless v is null or
   348  *     undefinedin which case bytes->ptr() remains null.
   349  */
   350 bool
   351 xpc_qsJsvalToCharStr(JSContext *cx, jsval v, JSAutoByteString *bytes);
   353 bool
   354 xpc_qsJsvalToWcharStr(JSContext *cx, jsval v, JS::MutableHandleValue pval, const char16_t **pstr);
   357 nsresult
   358 getWrapper(JSContext *cx,
   359            JSObject *obj,
   360            XPCWrappedNative **wrapper,
   361            JSObject **cur,
   362            XPCWrappedNativeTearOff **tearoff);
   364 nsresult
   365 castNative(JSContext *cx,
   366            XPCWrappedNative *wrapper,
   367            JSObject *cur,
   368            XPCWrappedNativeTearOff *tearoff,
   369            const nsIID &iid,
   370            void **ppThis,
   371            nsISupports **ppThisRef,
   372            JS::MutableHandleValue vp);
   374 /**
   375  * Search @a obj and its prototype chain for an XPCOM object that implements
   376  * the interface T.
   377  *
   378  * If an object implementing T is found, store a reference to the wrapper
   379  * JSObject in @a *pThisVal, store a pointer to the T in @a *ppThis, and return
   380  * true. Otherwise, raise an exception on @a cx and return false.
   381  *
   382  * @a *pThisRef receives the same pointer as *ppThis if the T was AddRefed.
   383  * Otherwise it receives null (even on error).
   384  *
   385  * This supports split objects and XPConnect tear-offs and it sees through
   386  * XOWs, XPCNativeWrappers, and SafeJSObjectWrappers.
   387  *
   388  * Requires a request on @a cx.
   389  */
   390 template <class T>
   391 inline bool
   392 xpc_qsUnwrapThis(JSContext *cx,
   393                  JS::HandleObject obj,
   394                  T **ppThis,
   395                  nsISupports **pThisRef,
   396                  JS::MutableHandleValue pThisVal,
   397                  bool failureFatal = true)
   398 {
   399     XPCWrappedNative *wrapper;
   400     XPCWrappedNativeTearOff *tearoff;
   401     JS::RootedObject current(cx);
   402     nsresult rv = getWrapper(cx, obj, &wrapper, current.address(), &tearoff);
   403     if (NS_SUCCEEDED(rv))
   404         rv = castNative(cx, wrapper, current, tearoff, NS_GET_TEMPLATE_IID(T),
   405                         reinterpret_cast<void **>(ppThis), pThisRef, pThisVal);
   407     if (failureFatal)
   408         return NS_SUCCEEDED(rv) || xpc_qsThrow(cx, rv);
   410     if (NS_FAILED(rv))
   411         *ppThis = nullptr;
   412     return true;
   413 }
   415 nsISupports*
   416 castNativeFromWrapper(JSContext *cx,
   417                       JSObject *obj,
   418                       uint32_t interfaceBit,
   419                       uint32_t protoID,
   420                       int32_t protoDepth,
   421                       nsISupports **pRef,
   422                       JS::MutableHandleValue pVal,
   423                       nsresult *rv);
   425 bool
   426 xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx,
   427                             const nsIID &iid,
   428                             void **ppThis,
   429                             nsISupports **pThisRef,
   430                             JS::MutableHandleValue vp);
   432 /**
   433  * Alternate implementation of xpc_qsUnwrapThis using information already
   434  * present in the given XPCCallContext.
   435  */
   436 template <class T>
   437 inline bool
   438 xpc_qsUnwrapThisFromCcx(XPCCallContext &ccx,
   439                         T **ppThis,
   440                         nsISupports **pThisRef,
   441                         JS::MutableHandleValue pThisVal)
   442 {
   443     return xpc_qsUnwrapThisFromCcxImpl(ccx,
   444                                        NS_GET_TEMPLATE_IID(T),
   445                                        reinterpret_cast<void **>(ppThis),
   446                                        pThisRef,
   447                                        pThisVal);
   448 }
   450 MOZ_ALWAYS_INLINE JSObject*
   451 xpc_qsUnwrapObj(jsval v, nsISupports **ppArgRef, nsresult *rv)
   452 {
   453     *rv = NS_OK;
   454     if (v.isObject()) {
   455         return &v.toObject();
   456     }
   458     if (!v.isNullOrUndefined()) {
   459         *rv = ((v.isInt32() && v.toInt32() == 0)
   460                ? NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL
   461                : NS_ERROR_XPC_BAD_CONVERT_JS);
   462     }
   464     *ppArgRef = nullptr;
   465     return nullptr;
   466 }
   468 nsresult
   469 xpc_qsUnwrapArgImpl(JSContext *cx, JS::HandleValue v, const nsIID &iid, void **ppArg,
   470                     nsISupports **ppArgRef, JS::MutableHandleValue vp);
   472 /** Convert a jsval to an XPCOM pointer. */
   473 template <class Interface, class StrongRefType>
   474 inline nsresult
   475 xpc_qsUnwrapArg(JSContext *cx, JS::HandleValue v, Interface **ppArg,
   476                 StrongRefType **ppArgRef, JS::MutableHandleValue vp)
   477 {
   478     nsISupports* argRef = *ppArgRef;
   479     nsresult rv = xpc_qsUnwrapArgImpl(cx, v, NS_GET_TEMPLATE_IID(Interface),
   480                                       reinterpret_cast<void **>(ppArg), &argRef,
   481                                       vp);
   482     *ppArgRef = static_cast<StrongRefType*>(argRef);
   483     return rv;
   484 }
   486 MOZ_ALWAYS_INLINE nsISupports*
   487 castNativeArgFromWrapper(JSContext *cx,
   488                          jsval v,
   489                          uint32_t bit,
   490                          uint32_t protoID,
   491                          int32_t protoDepth,
   492                          nsISupports **pArgRef,
   493                          JS::MutableHandleValue vp,
   494                          nsresult *rv)
   495 {
   496     JSObject *src = xpc_qsUnwrapObj(v, pArgRef, rv);
   497     if (!src)
   498         return nullptr;
   500     return castNativeFromWrapper(cx, src, bit, protoID, protoDepth, pArgRef, vp, rv);
   501 }
   503 inline nsWrapperCache*
   504 xpc_qsGetWrapperCache(nsWrapperCache *cache)
   505 {
   506     return cache;
   507 }
   509 inline nsWrapperCache*
   510 xpc_qsGetWrapperCache(void *p)
   511 {
   512     return nullptr;
   513 }
   515 /** Convert an XPCOM pointer to jsval. Return true on success.
   516  * aIdentity is a performance optimization. Set it to true,
   517  * only if p is the identity pointer.
   518  */
   519 bool
   520 xpc_qsXPCOMObjectToJsval(JSContext *aCx,
   521                          qsObjectHelper &aHelper,
   522                          const nsIID *iid,
   523                          XPCNativeInterface **iface,
   524                          JS::MutableHandleValue rval);
   526 /**
   527  * Convert a variant to jsval. Return true on success.
   528  */
   529 bool
   530 xpc_qsVariantToJsval(JSContext *cx,
   531                      nsIVariant *p,
   532                      JS::MutableHandleValue rval);
   534 #ifdef DEBUG
   535 void
   536 xpc_qsAssertContextOK(JSContext *cx);
   538 inline bool
   539 xpc_qsSameResult(nsISupports *result1, nsISupports *result2)
   540 {
   541     return SameCOMIdentity(result1, result2);
   542 }
   544 inline bool
   545 xpc_qsSameResult(const nsString &result1, const nsString &result2)
   546 {
   547     return result1.Equals(result2);
   548 }
   550 inline bool
   551 xpc_qsSameResult(int32_t result1, int32_t result2)
   552 {
   553     return result1 == result2;
   554 }
   556 #define XPC_QS_ASSERT_CONTEXT_OK(cx) xpc_qsAssertContextOK(cx)
   557 #else
   558 #define XPC_QS_ASSERT_CONTEXT_OK(cx) ((void) 0)
   559 #endif
   561 // Apply |op| to |obj|, |id|, and |vp|. If |op| is a setter, treat the assignment as lenient.
   562 template<typename Op>
   563 inline bool ApplyPropertyOp(JSContext *cx, Op op, JS::HandleObject obj, JS::HandleId id,
   564                               JS::MutableHandleValue vp);
   566 template<>
   567 inline bool
   568 ApplyPropertyOp<JSPropertyOp>(JSContext *cx, JSPropertyOp op, JS::HandleObject obj, JS::HandleId id,
   569                               JS::MutableHandleValue vp)
   570 {
   571     return op(cx, obj, id, vp);
   572 }
   574 template<>
   575 inline bool
   576 ApplyPropertyOp<JSStrictPropertyOp>(JSContext *cx, JSStrictPropertyOp op, JS::HandleObject obj,
   577                                     JS::HandleId id, JS::MutableHandleValue vp)
   578 {
   579     return op(cx, obj, id, true, vp);
   580 }
   582 template<typename Op>
   583 bool
   584 PropertyOpForwarder(JSContext *cx, unsigned argc, jsval *vp)
   585 {
   586     // Layout:
   587     //   this = our this
   588     //   property op to call = callee reserved slot 0
   589     //   name of the property = callee reserved slot 1
   591     JS::CallArgs args = CallArgsFromVp(argc, vp);
   593     JS::RootedObject callee(cx, &args.callee());
   594     JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   595     if (!obj)
   596         return false;
   598     JS::RootedValue v(cx, js::GetFunctionNativeReserved(callee, 0));
   600     JSObject *ptrobj = JSVAL_TO_OBJECT(v);
   601     Op *popp = static_cast<Op *>(JS_GetPrivate(ptrobj));
   603     v = js::GetFunctionNativeReserved(callee, 1);
   605     JS::RootedValue argval(cx, args.get(0));
   606     JS::RootedId id(cx);
   607     if (!JS_ValueToId(cx, v, &id))
   608         return false;
   609     args.rval().set(argval);
   610     return ApplyPropertyOp<Op>(cx, *popp, obj, id, args.rval());
   611 }
   613 extern const JSClass PointerHolderClass;
   615 template<typename Op>
   616 JSObject *
   617 GeneratePropertyOp(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned argc, Op pop)
   618 {
   619     // The JS engine provides two reserved slots on function objects for
   620     // XPConnect to use. Use them to stick the necessary info here.
   621     JSFunction *fun =
   622         js::NewFunctionByIdWithReserved(cx, PropertyOpForwarder<Op>, argc, 0, obj, id);
   623     if (!fun)
   624         return nullptr;
   626     JS::RootedObject funobj(cx, JS_GetFunctionObject(fun));
   628     // Unfortunately, we cannot guarantee that Op is aligned. Use a
   629     // second object to work around this.
   630     JSObject *ptrobj = JS_NewObject(cx, &PointerHolderClass, JS::NullPtr(), funobj);
   631     if (!ptrobj)
   632         return nullptr;
   633     Op *popp = new Op;
   634     if (!popp)
   635         return nullptr;
   636     *popp = pop;
   637     JS_SetPrivate(ptrobj, popp);
   639     js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(ptrobj));
   640     js::SetFunctionNativeReserved(funobj, 1, js::IdToValue(id));
   641     return funobj;
   642 }
   644 #endif /* xpcquickstubs_h___ */

mercurial