1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Foundation
19 : * Portions created by the Initial Developer are Copyright (C) 2008
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jason Orendorff <jorendorff@mozilla.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Util.h"
40 :
41 : #include "jsapi.h"
42 : #include "jsatom.h"
43 : #include "jsfriendapi.h"
44 : #include "nsCOMPtr.h"
45 : #include "xpcprivate.h"
46 : #include "XPCInlines.h"
47 : #include "XPCQuickStubs.h"
48 : #include "XPCWrapper.h"
49 :
50 : using namespace mozilla;
51 :
52 : static inline QITableEntry *
53 2880 : GetOffsets(nsISupports *identity, XPCWrappedNativeProto* proto)
54 : {
55 2880 : QITableEntry* offsets = proto ? proto->GetOffsets() : nsnull;
56 2880 : if (!offsets) {
57 : static NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
58 743 : identity->QueryInterface(kThisPtrOffsetsSID, (void**)&offsets);
59 : }
60 2880 : return offsets;
61 : }
62 :
63 : static inline QITableEntry *
64 2284 : GetOffsetsFromSlimWrapper(JSObject *obj)
65 : {
66 2284 : NS_ASSERTION(IS_SLIM_WRAPPER(obj), "What kind of object is this?");
67 2284 : return GetOffsets(static_cast<nsISupports*>(xpc_GetJSPrivate(obj)),
68 4568 : GetSlimWrapperProto(obj));
69 : }
70 :
71 : static const xpc_qsHashEntry *
72 14846 : LookupEntry(PRUint32 tableSize, const xpc_qsHashEntry *table, const nsID &iid)
73 : {
74 : size_t i;
75 : const xpc_qsHashEntry *p;
76 :
77 14846 : i = iid.m0 % tableSize;
78 9239 : do
79 : {
80 17474 : p = table + i;
81 17474 : if (p->iid.Equals(iid))
82 8235 : return p;
83 9239 : i = p->chain;
84 : } while (i != XPC_QS_NULL_INDEX);
85 6611 : return nsnull;
86 : }
87 :
88 : static const xpc_qsHashEntry *
89 11102 : LookupInterfaceOrAncestor(PRUint32 tableSize, const xpc_qsHashEntry *table,
90 : const nsID &iid)
91 : {
92 11102 : const xpc_qsHashEntry *entry = LookupEntry(tableSize, table, iid);
93 11102 : if (!entry) {
94 : /*
95 : * On a miss, we have to search for every interface the object
96 : * supports, including ancestors.
97 : */
98 7484 : nsCOMPtr<nsIInterfaceInfo> info;
99 3742 : if (NS_FAILED(nsXPConnect::GetXPConnect()->GetInfoForIID(&iid, getter_AddRefs(info))))
100 0 : return nsnull;
101 :
102 : const nsIID *piid;
103 2869 : for (;;) {
104 13222 : nsCOMPtr<nsIInterfaceInfo> parent;
105 16966 : if (NS_FAILED(info->GetParent(getter_AddRefs(parent))) ||
106 6611 : !parent ||
107 3744 : NS_FAILED(parent->GetIIDShared(&piid))) {
108 : break;
109 : }
110 3744 : entry = LookupEntry(tableSize, table, *piid);
111 3744 : if (entry)
112 : break;
113 9480 : info.swap(parent);
114 : }
115 : }
116 11102 : return entry;
117 : }
118 :
119 : // Apply |op| to |obj|, |id|, and |vp|. If |op| is a setter, treat the assignment as lenient.
120 : template<typename Op>
121 : static inline JSBool ApplyPropertyOp(JSContext *cx, Op op, JSObject *obj, jsid id, jsval *vp);
122 :
123 : template<>
124 : inline JSBool
125 0 : ApplyPropertyOp<JSPropertyOp>(JSContext *cx, JSPropertyOp op, JSObject *obj, jsid id, jsval *vp)
126 : {
127 0 : return op(cx, obj, id, vp);
128 : }
129 :
130 : template<>
131 : inline JSBool
132 0 : ApplyPropertyOp<JSStrictPropertyOp>(JSContext *cx, JSStrictPropertyOp op, JSObject *obj,
133 : jsid id, jsval *vp)
134 : {
135 0 : return op(cx, obj, id, true, vp);
136 : }
137 :
138 : template<typename Op>
139 : static JSBool
140 0 : PropertyOpForwarder(JSContext *cx, unsigned argc, jsval *vp)
141 : {
142 : // Layout:
143 : // this = our this
144 : // property op to call = callee reserved slot 0
145 : // name of the property = callee reserved slot 1
146 :
147 0 : JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
148 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
149 0 : if (!obj)
150 0 : return false;
151 :
152 0 : jsval v = js::GetFunctionNativeReserved(callee, 0);
153 :
154 0 : JSObject *ptrobj = JSVAL_TO_OBJECT(v);
155 0 : Op *popp = static_cast<Op *>(JS_GetPrivate(ptrobj));
156 :
157 0 : v = js::GetFunctionNativeReserved(callee, 1);
158 :
159 0 : jsval argval = (argc > 0) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
160 : jsid id;
161 0 : if (!JS_ValueToId(cx, argval, &id))
162 0 : return false;
163 0 : JS_SET_RVAL(cx, vp, argval);
164 0 : return ApplyPropertyOp<Op>(cx, *popp, obj, id, vp);
165 : }
166 :
167 : static void
168 0 : PointerFinalize(JSContext *cx, JSObject *obj)
169 : {
170 0 : JSPropertyOp *popp = static_cast<JSPropertyOp *>(JS_GetPrivate(obj));
171 : delete popp;
172 0 : }
173 :
174 : static JSClass
175 : PointerHolderClass = {
176 : "Pointer", JSCLASS_HAS_PRIVATE,
177 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
178 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize,
179 : JSCLASS_NO_OPTIONAL_MEMBERS
180 : };
181 :
182 : template<typename Op>
183 : static JSObject *
184 0 : GeneratePropertyOp(JSContext *cx, JSObject *obj, jsid id, unsigned argc, Op pop)
185 : {
186 : // The JS engine provides two reserved slots on function objects for
187 : // XPConnect to use. Use them to stick the necessary info here.
188 : JSFunction *fun =
189 0 : js::NewFunctionByIdWithReserved(cx, PropertyOpForwarder<Op>, argc, 0, obj, id);
190 0 : if (!fun)
191 0 : return nsnull;
192 :
193 0 : JSObject *funobj = JS_GetFunctionObject(fun);
194 :
195 0 : JS::AutoObjectRooter tvr(cx, funobj);
196 :
197 : // Unfortunately, we cannot guarantee that Op is aligned. Use a
198 : // second object to work around this.
199 0 : JSObject *ptrobj = JS_NewObject(cx, &PointerHolderClass, nsnull, funobj);
200 0 : if (!ptrobj)
201 0 : return nsnull;
202 0 : Op *popp = new Op;
203 0 : if (!popp)
204 0 : return nsnull;
205 0 : *popp = pop;
206 0 : JS_SetPrivate(ptrobj, popp);
207 :
208 0 : js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(ptrobj));
209 0 : js::SetFunctionNativeReserved(funobj, 1, js::IdToJsval(id));
210 0 : return funobj;
211 : }
212 :
213 : static JSBool
214 0 : ReifyPropertyOps(JSContext *cx, JSObject *obj, jsid id, unsigned orig_attrs,
215 : JSPropertyOp getter, JSStrictPropertyOp setter,
216 : JSObject **getterobjp, JSObject **setterobjp)
217 : {
218 : // Generate both getter and setter and stash them in the prototype.
219 0 : jsval roots[2] = { JSVAL_NULL, JSVAL_NULL };
220 0 : JS::AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
221 :
222 0 : unsigned attrs = JSPROP_SHARED | (orig_attrs & JSPROP_ENUMERATE);
223 : JSObject *getterobj;
224 0 : if (getter) {
225 0 : getterobj = GeneratePropertyOp(cx, obj, id, 0, getter);
226 0 : if (!getterobj)
227 0 : return false;
228 0 : roots[0] = OBJECT_TO_JSVAL(getterobj);
229 0 : attrs |= JSPROP_GETTER;
230 : } else
231 0 : getterobj = nsnull;
232 :
233 : JSObject *setterobj;
234 0 : if (setter) {
235 0 : setterobj = GeneratePropertyOp(cx, obj, id, 1, setter);
236 0 : if (!setterobj)
237 0 : return false;
238 0 : roots[1] = OBJECT_TO_JSVAL(setterobj);
239 0 : attrs |= JSPROP_SETTER;
240 : } else
241 0 : setterobj = nsnull;
242 :
243 0 : if (getterobjp)
244 0 : *getterobjp = getterobj;
245 0 : if (setterobjp)
246 0 : *setterobjp = setterobj;
247 : return JS_DefinePropertyById(cx, obj, id, JSVAL_VOID,
248 : JS_DATA_TO_FUNC_PTR(JSPropertyOp, getterobj),
249 : JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setterobj),
250 0 : attrs);
251 : }
252 :
253 : static JSBool
254 0 : LookupGetterOrSetter(JSContext *cx, JSBool wantGetter, unsigned argc, jsval *vp)
255 : {
256 0 : XPC_QS_ASSERT_CONTEXT_OK(cx);
257 :
258 0 : if (argc == 0) {
259 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
260 0 : return true;
261 : }
262 :
263 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
264 0 : if (!obj)
265 0 : return false;
266 :
267 0 : jsval idval = JS_ARGV(cx, vp)[0];
268 : jsid id;
269 : JSPropertyDescriptor desc;
270 0 : if (!JS_ValueToId(cx, idval, &id) ||
271 0 : !JS_GetPropertyDescriptorById(cx, obj, id, JSRESOLVE_QUALIFIED, &desc))
272 0 : return false;
273 :
274 : // No property at all means no getters or setters possible.
275 0 : if (!desc.obj) {
276 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
277 0 : return true;
278 : }
279 :
280 : // Inline obj_lookup[GS]etter here.
281 0 : if (wantGetter) {
282 0 : if (desc.attrs & JSPROP_GETTER) {
283 0 : JS_SET_RVAL(cx, vp,
284 0 : OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, desc.getter)));
285 0 : return true;
286 : }
287 : } else {
288 0 : if (desc.attrs & JSPROP_SETTER) {
289 0 : JS_SET_RVAL(cx, vp,
290 0 : OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, desc.setter)));
291 0 : return true;
292 : }
293 : }
294 :
295 : // Since XPConnect doesn't use JSPropertyOps in any other contexts,
296 : // ensuring that we have an XPConnect prototype object ensures that
297 : // we are only going to expose quickstubbed properties to script.
298 : // Also be careful not to overwrite existing properties!
299 :
300 0 : if (!JSID_IS_STRING(id) ||
301 0 : !IS_PROTO_CLASS(js::GetObjectClass(desc.obj)) ||
302 : (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) ||
303 0 : !(desc.getter || desc.setter) ||
304 0 : desc.setter == js::GetObjectJSClass(desc.obj)->setProperty) {
305 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
306 0 : return true;
307 : }
308 :
309 : JSObject *getterobj, *setterobj;
310 0 : if (!ReifyPropertyOps(cx, desc.obj, id, desc.attrs, desc.getter, desc.setter,
311 0 : &getterobj, &setterobj)) {
312 0 : return false;
313 : }
314 :
315 0 : JSObject *wantedobj = wantGetter ? getterobj : setterobj;
316 0 : jsval v = wantedobj ? OBJECT_TO_JSVAL(wantedobj) : JSVAL_VOID;
317 0 : JS_SET_RVAL(cx, vp, v);
318 0 : return true;
319 : }
320 :
321 : static JSBool
322 0 : SharedLookupGetter(JSContext *cx, unsigned argc, jsval *vp)
323 : {
324 0 : return LookupGetterOrSetter(cx, true, argc, vp);
325 : }
326 :
327 : static JSBool
328 0 : SharedLookupSetter(JSContext *cx, unsigned argc, jsval *vp)
329 : {
330 0 : return LookupGetterOrSetter(cx, false, argc, vp);
331 : }
332 :
333 : static JSBool
334 0 : DefineGetterOrSetter(JSContext *cx, unsigned argc, JSBool wantGetter, jsval *vp)
335 : {
336 : unsigned attrs;
337 : JSBool found;
338 : JSPropertyOp getter;
339 : JSStrictPropertyOp setter;
340 : JSObject *obj2;
341 : jsval v;
342 : jsid id;
343 :
344 0 : XPC_QS_ASSERT_CONTEXT_OK(cx);
345 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
346 0 : if (!obj)
347 0 : return false;
348 0 : JSNative forward = wantGetter ? js::obj_defineGetter : js::obj_defineSetter;
349 0 : jsval idval = (argc >= 1) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
350 0 : if (!JSVAL_IS_STRING(idval))
351 0 : return forward(cx, argc, vp);
352 :
353 0 : if (!JS_ValueToId(cx, idval, &id) ||
354 : !JS_LookupPropertyWithFlagsById(cx, obj, id,
355 0 : JSRESOLVE_QUALIFIED, &obj2, &v) ||
356 : (obj2 &&
357 : !JS_GetPropertyAttrsGetterAndSetterById(cx, obj2, id, &attrs,
358 0 : &found, &getter, &setter)))
359 0 : return false;
360 :
361 : // The property didn't exist, already has a getter or setter, or is not
362 : // our property, then just forward now.
363 0 : if (!obj2 ||
364 : (attrs & (JSPROP_GETTER | JSPROP_SETTER)) ||
365 0 : !(getter || setter) ||
366 0 : !IS_PROTO_CLASS(js::GetObjectClass(obj2)))
367 0 : return forward(cx, argc, vp);
368 :
369 : // Reify the getter and setter...
370 0 : if (!ReifyPropertyOps(cx, obj2, id, attrs, getter, setter, nsnull, nsnull))
371 0 : return false;
372 :
373 0 : return forward(cx, argc, vp);
374 : }
375 :
376 : static JSBool
377 0 : SharedDefineGetter(JSContext *cx, unsigned argc, jsval *vp)
378 : {
379 0 : return DefineGetterOrSetter(cx, argc, true, vp);
380 : }
381 :
382 : static JSBool
383 0 : SharedDefineSetter(JSContext *cx, unsigned argc, jsval *vp)
384 : {
385 0 : return DefineGetterOrSetter(cx, argc, false, vp);
386 : }
387 :
388 :
389 : JSBool
390 2781 : xpc_qsDefineQuickStubs(JSContext *cx, JSObject *proto, unsigned flags,
391 : PRUint32 ifacec, const nsIID **interfaces,
392 : PRUint32 tableSize, const xpc_qsHashEntry *table,
393 : const xpc_qsPropertySpec *propspecs,
394 : const xpc_qsFunctionSpec *funcspecs,
395 : const char *stringTable)
396 : {
397 : /*
398 : * Walk interfaces in reverse order to behave like XPConnect when a
399 : * feature is defined in more than one of the interfaces.
400 : *
401 : * XPCNativeSet::FindMethod returns the first matching feature it finds,
402 : * searching the interfaces forward. Here, definitions toward the
403 : * front of 'interfaces' overwrite those toward the back.
404 : */
405 2781 : bool definedProperty = false;
406 16664 : for (uint32_t i = ifacec; i-- != 0;) {
407 11102 : const nsID &iid = *interfaces[i];
408 : const xpc_qsHashEntry *entry =
409 11102 : LookupInterfaceOrAncestor(tableSize, table, iid);
410 :
411 11102 : if (entry) {
412 2627 : for (;;) {
413 : // Define quick stubs for attributes.
414 10862 : const xpc_qsPropertySpec *ps = propspecs + entry->prop_index;
415 10862 : const xpc_qsPropertySpec *ps_end = ps + entry->n_props;
416 80876 : for ( ; ps < ps_end; ++ps) {
417 70014 : definedProperty = true;
418 70014 : if (!JS_DefineProperty(cx, proto,
419 : stringTable + ps->name_index,
420 : JSVAL_VOID, ps->getter, ps->setter,
421 70014 : flags | JSPROP_SHARED))
422 0 : return false;
423 : }
424 :
425 : // Define quick stubs for methods.
426 10862 : const xpc_qsFunctionSpec *fs = funcspecs + entry->func_index;
427 10862 : const xpc_qsFunctionSpec *fs_end = fs + entry->n_funcs;
428 87438 : for ( ; fs < fs_end; ++fs) {
429 76576 : if (!JS_DefineFunction(cx, proto,
430 : stringTable + fs->name_index,
431 : reinterpret_cast<JSNative>(fs->native),
432 76576 : fs->arity, flags))
433 0 : return false;
434 : }
435 :
436 : // Next.
437 10862 : size_t j = entry->parentInterface;
438 10862 : if (j == XPC_QS_NULL_INDEX)
439 : break;
440 2627 : entry = table + j;
441 : }
442 : }
443 : }
444 :
445 : static JSFunctionSpec getterfns[] = {
446 : JS_FN("__lookupGetter__", SharedLookupGetter, 1, 0),
447 : JS_FN("__lookupSetter__", SharedLookupSetter, 1, 0),
448 : JS_FN("__defineGetter__", SharedDefineGetter, 2, 0),
449 : JS_FN("__defineSetter__", SharedDefineSetter, 2, 0),
450 : JS_FS_END
451 : };
452 :
453 2781 : if (definedProperty && !JS_DefineFunctions(cx, proto, getterfns))
454 0 : return false;
455 :
456 2781 : return true;
457 : }
458 :
459 : JSBool
460 10 : xpc_qsThrow(JSContext *cx, nsresult rv)
461 : {
462 10 : XPCThrower::Throw(rv, cx);
463 10 : return false;
464 : }
465 :
466 : /**
467 : * Get the interface name and member name (for error messages).
468 : *
469 : * We could instead have each quick stub pass its name to the error-handling
470 : * functions, as that name is statically known. But that would be redundant;
471 : * the information is handy at runtime anyway. Also, this code often produces
472 : * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]"
473 : * rather than "[nsIDOMNode.appendChild]".
474 : */
475 : static void
476 229 : GetMemberInfo(JSObject *obj, jsid memberId, const char **ifaceName)
477 : {
478 : // Get the interface name. From DefinePropertyIfFound (in
479 : // xpcwrappednativejsops.cpp) and XPCThrower::Verbosify.
480 : //
481 : // We could instead make the quick stub could pass in its interface name,
482 : // but this code often produces a more specific error message, e.g.
483 229 : *ifaceName = "Unknown";
484 :
485 229 : NS_ASSERTION(IS_WRAPPER_CLASS(js::GetObjectClass(obj)) ||
486 : js::GetObjectClass(obj) == &XPC_WN_Tearoff_JSClass,
487 : "obj must be a wrapper");
488 : XPCWrappedNativeProto *proto;
489 229 : if (IS_SLIM_WRAPPER(obj)) {
490 0 : proto = GetSlimWrapperProto(obj);
491 : } else {
492 229 : XPCWrappedNative *wrapper = (XPCWrappedNative *) js::GetObjectPrivate(obj);
493 229 : proto = wrapper->GetProto();
494 : }
495 229 : if (proto) {
496 229 : XPCNativeSet *set = proto->GetSet();
497 229 : if (set) {
498 : XPCNativeMember *member;
499 : XPCNativeInterface *iface;
500 :
501 229 : if (set->FindMember(memberId, &member, &iface))
502 229 : *ifaceName = iface->GetNameString();
503 : }
504 : }
505 229 : }
506 :
507 : static void
508 228 : GetMethodInfo(JSContext *cx, jsval *vp, const char **ifaceNamep, jsid *memberIdp)
509 : {
510 228 : JSObject *funobj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
511 228 : NS_ASSERTION(JS_ObjectIsFunction(cx, funobj),
512 : "JSNative callee should be Function object");
513 228 : JSString *str = JS_GetFunctionId(JS_GetObjectFunction(funobj));
514 228 : jsid methodId = str ? INTERNED_STRING_TO_JSID(cx, str) : JSID_VOID;
515 228 : GetMemberInfo(JSVAL_TO_OBJECT(vp[1]), methodId, ifaceNamep);
516 228 : *memberIdp = methodId;
517 228 : }
518 :
519 : static bool
520 229 : ThrowCallFailed(JSContext *cx, nsresult rv,
521 : const char *ifaceName, jsid memberId, const char *memberName)
522 : {
523 : /* Only one of memberId or memberName should be given. */
524 229 : JS_ASSERT(JSID_IS_VOID(memberId) != !memberName);
525 :
526 : // From XPCThrower::ThrowBadResult.
527 : char* sz;
528 : const char* format;
529 : const char* name;
530 :
531 : /*
532 : * If there is a pending exception when the native call returns and
533 : * it has the same error result as returned by the native call, then
534 : * the native call may be passing through an error from a previous JS
535 : * call. So we'll just throw that exception into our JS.
536 : */
537 229 : if (XPCThrower::CheckForPendingException(rv, cx))
538 0 : return false;
539 :
540 : // else...
541 :
542 458 : if (!nsXPCException::NameAndFormatForNSResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, nsnull, &format) ||
543 229 : !format) {
544 0 : format = "";
545 : }
546 :
547 458 : JSAutoByteString memberNameBytes;
548 229 : if (!memberName) {
549 229 : memberName = JSID_IS_STRING(memberId)
550 229 : ? memberNameBytes.encode(cx, JSID_TO_STRING(memberId))
551 458 : : "unknown";
552 : }
553 229 : if (nsXPCException::NameAndFormatForNSResult(rv, &name, nsnull)
554 : && name) {
555 : sz = JS_smprintf("%s 0x%x (%s) [%s.%s]",
556 3 : format, rv, name, ifaceName, memberName);
557 : } else {
558 : sz = JS_smprintf("%s 0x%x [%s.%s]",
559 226 : format, rv, ifaceName, memberName);
560 : }
561 :
562 229 : XPCThrower::BuildAndThrowException(cx, rv, sz);
563 :
564 229 : if (sz)
565 229 : JS_smprintf_free(sz);
566 :
567 229 : return false;
568 : }
569 :
570 : JSBool
571 1 : xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj,
572 : jsid memberId)
573 : {
574 : const char *ifaceName;
575 1 : GetMemberInfo(obj, memberId, &ifaceName);
576 1 : return ThrowCallFailed(cx, rv, ifaceName, memberId, NULL);
577 : }
578 :
579 : JSBool
580 228 : xpc_qsThrowMethodFailed(JSContext *cx, nsresult rv, jsval *vp)
581 : {
582 : const char *ifaceName;
583 : jsid memberId;
584 228 : GetMethodInfo(cx, vp, &ifaceName, &memberId);
585 228 : return ThrowCallFailed(cx, rv, ifaceName, memberId, NULL);
586 : }
587 :
588 : JSBool
589 0 : xpc_qsThrowMethodFailedWithCcx(XPCCallContext &ccx, nsresult rv)
590 : {
591 0 : ThrowBadResult(rv, ccx);
592 0 : return false;
593 : }
594 :
595 : bool
596 0 : xpc_qsThrowMethodFailedWithDetails(JSContext *cx, nsresult rv,
597 : const char *ifaceName,
598 : const char *memberName)
599 : {
600 0 : return ThrowCallFailed(cx, rv, ifaceName, JSID_VOID, memberName);
601 : }
602 :
603 : static void
604 0 : ThrowBadArg(JSContext *cx, nsresult rv, const char *ifaceName,
605 : jsid memberId, const char *memberName, unsigned paramnum)
606 : {
607 : /* Only one memberId or memberName should be given. */
608 0 : JS_ASSERT(JSID_IS_VOID(memberId) != !memberName);
609 :
610 : // From XPCThrower::ThrowBadParam.
611 : char* sz;
612 : const char* format;
613 :
614 0 : if (!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
615 0 : format = "";
616 :
617 0 : JSAutoByteString memberNameBytes;
618 0 : if (!memberName) {
619 0 : memberName = JSID_IS_STRING(memberId)
620 0 : ? memberNameBytes.encode(cx, JSID_TO_STRING(memberId))
621 0 : : "unknown";
622 : }
623 : sz = JS_smprintf("%s arg %u [%s.%s]",
624 0 : format, (unsigned int) paramnum, ifaceName, memberName);
625 :
626 0 : XPCThrower::BuildAndThrowException(cx, rv, sz);
627 :
628 0 : if (sz)
629 0 : JS_smprintf_free(sz);
630 0 : }
631 :
632 : void
633 0 : xpc_qsThrowBadArg(JSContext *cx, nsresult rv, jsval *vp, unsigned paramnum)
634 : {
635 : const char *ifaceName;
636 : jsid memberId;
637 0 : GetMethodInfo(cx, vp, &ifaceName, &memberId);
638 0 : ThrowBadArg(cx, rv, ifaceName, memberId, NULL, paramnum);
639 0 : }
640 :
641 : void
642 0 : xpc_qsThrowBadArgWithCcx(XPCCallContext &ccx, nsresult rv, unsigned paramnum)
643 : {
644 0 : XPCThrower::ThrowBadParam(rv, paramnum, ccx);
645 0 : }
646 :
647 : void
648 0 : xpc_qsThrowBadArgWithDetails(JSContext *cx, nsresult rv, unsigned paramnum,
649 : const char *ifaceName, const char *memberName)
650 : {
651 0 : ThrowBadArg(cx, rv, ifaceName, JSID_VOID, memberName, paramnum);
652 0 : }
653 :
654 : void
655 0 : xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
656 : JSObject *obj, jsid propId)
657 : {
658 : const char *ifaceName;
659 0 : GetMemberInfo(obj, propId, &ifaceName);
660 0 : ThrowBadArg(cx, rv, ifaceName, propId, NULL, 0);
661 0 : }
662 :
663 : JSBool
664 0 : xpc_qsGetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
665 : {
666 : return JS_ReportErrorFlagsAndNumber(cx,
667 : JSREPORT_WARNING | JSREPORT_STRICT |
668 : JSREPORT_STRICT_MODE_ERROR,
669 : js_GetErrorMessage, NULL,
670 0 : JSMSG_GETTER_ONLY);
671 : }
672 :
673 17378 : xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval,
674 : StringificationBehavior nullBehavior,
675 17378 : StringificationBehavior undefinedBehavior)
676 : {
677 : typedef implementation_type::char_traits traits;
678 : // From the T_DOMSTRING case in XPCConvert::JSData2Native.
679 : JSString *s = InitOrStringify<traits>(cx, v, pval, nullBehavior,
680 17378 : undefinedBehavior);
681 17378 : if (!s)
682 1554 : return;
683 :
684 : size_t len;
685 15824 : const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
686 15824 : if (!chars) {
687 0 : mValid = false;
688 0 : return;
689 : }
690 :
691 15824 : new(mBuf) implementation_type(chars, len);
692 15824 : mValid = true;
693 : }
694 :
695 7833 : xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval,
696 : StringificationBehavior nullBehavior,
697 7833 : StringificationBehavior undefinedBehavior)
698 : {
699 : typedef implementation_type::char_traits traits;
700 : // From the T_CSTRING case in XPCConvert::JSData2Native.
701 : JSString *s = InitOrStringify<traits>(cx, v, pval, nullBehavior,
702 7833 : undefinedBehavior);
703 7833 : if (!s)
704 0 : return;
705 :
706 7833 : size_t len = JS_GetStringEncodingLength(cx, s);
707 7833 : if (len == size_t(-1)) {
708 0 : mValid = false;
709 0 : return;
710 : }
711 :
712 15666 : JSAutoByteString bytes(cx, s);
713 7833 : if (!bytes) {
714 0 : mValid = false;
715 : return;
716 : }
717 :
718 7833 : new(mBuf) implementation_type(bytes.ptr(), len);
719 7833 : mValid = true;
720 : }
721 :
722 1194 : xpc_qsAUTF8String::xpc_qsAUTF8String(JSContext *cx, jsval v, jsval *pval)
723 : {
724 : typedef nsCharTraits<PRUnichar> traits;
725 : // From the T_UTF8STRING case in XPCConvert::JSData2Native.
726 1194 : JSString *s = InitOrStringify<traits>(cx, v, pval, eNull, eNull);
727 1194 : if (!s)
728 0 : return;
729 :
730 : size_t len;
731 1194 : const PRUnichar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
732 1194 : if (!chars) {
733 0 : mValid = false;
734 0 : return;
735 : }
736 :
737 1194 : new(mBuf) implementation_type(chars, len);
738 1194 : mValid = true;
739 : }
740 :
741 : static nsresult
742 33559 : getNative(nsISupports *idobj,
743 : QITableEntry* entries,
744 : JSObject *obj,
745 : const nsIID &iid,
746 : void **ppThis,
747 : nsISupports **pThisRef,
748 : jsval *vp)
749 : {
750 : // Try using the QITableEntry to avoid the extra AddRef and Release.
751 33559 : if (entries) {
752 18803 : for (QITableEntry* e = entries; e->iid; e++) {
753 18759 : if (e->iid->Equals(iid)) {
754 6864 : *ppThis = (char*) idobj + e->offset - entries[0].offset;
755 6864 : *vp = OBJECT_TO_JSVAL(obj);
756 6864 : *pThisRef = nsnull;
757 6864 : return NS_OK;
758 : }
759 : }
760 : }
761 :
762 26695 : nsresult rv = idobj->QueryInterface(iid, ppThis);
763 26695 : *pThisRef = static_cast<nsISupports*>(*ppThis);
764 26695 : if (NS_SUCCEEDED(rv))
765 26695 : *vp = OBJECT_TO_JSVAL(obj);
766 26695 : return rv;
767 : }
768 :
769 : inline nsresult
770 30679 : getNativeFromWrapper(JSContext *cx,
771 : XPCWrappedNative *wrapper,
772 : const nsIID &iid,
773 : void **ppThis,
774 : nsISupports **pThisRef,
775 : jsval *vp)
776 : {
777 : return getNative(wrapper->GetIdentityObject(), wrapper->GetOffsets(),
778 30679 : wrapper->GetFlatJSObject(), iid, ppThis, pThisRef, vp);
779 : }
780 :
781 :
782 : nsresult
783 41085 : getWrapper(JSContext *cx,
784 : JSObject *obj,
785 : JSObject *callee,
786 : XPCWrappedNative **wrapper,
787 : JSObject **cur,
788 : XPCWrappedNativeTearOff **tearoff)
789 : {
790 41085 : if (XPCWrapper::IsSecurityWrapper(obj) &&
791 : !(obj = XPCWrapper::Unwrap(cx, obj, false))) {
792 0 : return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
793 : }
794 :
795 41085 : *cur = obj;
796 41085 : *tearoff = nsnull;
797 :
798 : *wrapper =
799 : XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj, callee, cur,
800 41085 : tearoff);
801 :
802 41085 : return NS_OK;
803 : }
804 :
805 : nsresult
806 32963 : castNative(JSContext *cx,
807 : XPCWrappedNative *wrapper,
808 : JSObject *cur,
809 : XPCWrappedNativeTearOff *tearoff,
810 : const nsIID &iid,
811 : void **ppThis,
812 : nsISupports **pThisRef,
813 : jsval *vp,
814 : XPCLazyCallContext *lccx)
815 : {
816 32963 : if (wrapper) {
817 : nsresult rv = getNativeFromWrapper(cx,wrapper, iid, ppThis, pThisRef,
818 30679 : vp);
819 :
820 30679 : if (lccx && NS_SUCCEEDED(rv))
821 11976 : lccx->SetWrapper(wrapper, tearoff);
822 :
823 30679 : if (rv != NS_ERROR_NO_INTERFACE)
824 30679 : return rv;
825 2284 : } else if (cur) {
826 : nsISupports *native;
827 : QITableEntry *entries;
828 2284 : if (IS_SLIM_WRAPPER(cur)) {
829 2284 : native = static_cast<nsISupports*>(xpc_GetJSPrivate(cur));
830 2284 : entries = GetOffsetsFromSlimWrapper(cur);
831 : } else {
832 0 : NS_ABORT_IF_FALSE(mozilla::dom::binding::instanceIsProxy(cur),
833 : "what kind of wrapper is this?");
834 0 : native = static_cast<nsISupports*>(js::GetProxyPrivate(cur).toPrivate());
835 0 : entries = nsnull;
836 : }
837 :
838 2284 : if (NS_SUCCEEDED(getNative(native, entries, cur, iid, ppThis, pThisRef, vp))) {
839 2284 : if (lccx) {
840 : // This only matters for unwrapping of this objects, so we
841 : // shouldn't end up here for the new DOM bindings.
842 412 : NS_ABORT_IF_FALSE(IS_SLIM_WRAPPER(cur),
843 : "what kind of wrapper is this?");
844 412 : lccx->SetWrapper(cur);
845 : }
846 :
847 2284 : return NS_OK;
848 : }
849 : }
850 :
851 0 : *pThisRef = nsnull;
852 0 : return NS_ERROR_XPC_BAD_OP_ON_WN_PROTO;
853 : }
854 :
855 : JSBool
856 596 : xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx,
857 : const nsIID &iid,
858 : void **ppThis,
859 : nsISupports **pThisRef,
860 : jsval *vp)
861 : {
862 596 : nsISupports *native = ccx.GetIdentityObject();
863 596 : if (!native)
864 0 : return xpc_qsThrow(ccx.GetJSContext(), NS_ERROR_XPC_HAS_BEEN_SHUTDOWN);
865 :
866 : nsresult rv = getNative(native, GetOffsets(native, ccx.GetProto()),
867 : ccx.GetFlattenedJSObject(), iid, ppThis, pThisRef,
868 596 : vp);
869 596 : if (NS_FAILED(rv))
870 0 : return xpc_qsThrow(ccx.GetJSContext(), rv);
871 596 : return true;
872 : }
873 :
874 : JSObject*
875 6308 : xpc_qsUnwrapObj(jsval v, nsISupports **ppArgRef, nsresult *rv)
876 : {
877 6308 : if (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)) {
878 87 : *ppArgRef = nsnull;
879 87 : *rv = NS_OK;
880 87 : return nsnull;
881 : }
882 :
883 6221 : if (!JSVAL_IS_OBJECT(v)) {
884 0 : *ppArgRef = nsnull;
885 0 : *rv = ((JSVAL_IS_INT(v) && JSVAL_TO_INT(v) == 0)
886 : ? NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL
887 0 : : NS_ERROR_XPC_BAD_CONVERT_JS);
888 0 : return nsnull;
889 : }
890 :
891 6221 : *rv = NS_OK;
892 6221 : return JSVAL_TO_OBJECT(v);
893 : }
894 :
895 : nsresult
896 4785 : xpc_qsUnwrapArgImpl(JSContext *cx,
897 : jsval v,
898 : const nsIID &iid,
899 : void **ppArg,
900 : nsISupports **ppArgRef,
901 : jsval *vp)
902 : {
903 : nsresult rv;
904 4785 : JSObject *src = xpc_qsUnwrapObj(v, ppArgRef, &rv);
905 4785 : if (!src) {
906 87 : *ppArg = nsnull;
907 :
908 87 : return rv;
909 : }
910 :
911 : XPCWrappedNative *wrapper;
912 : XPCWrappedNativeTearOff *tearoff;
913 : JSObject *obj2;
914 4698 : if (mozilla::dom::binding::instanceIsProxy(src)) {
915 0 : wrapper = nsnull;
916 0 : obj2 = src;
917 : } else {
918 4698 : rv = getWrapper(cx, src, nsnull, &wrapper, &obj2, &tearoff);
919 4698 : NS_ENSURE_SUCCESS(rv, rv);
920 : }
921 :
922 4698 : if (wrapper || obj2) {
923 111 : if (NS_FAILED(castNative(cx, wrapper, obj2, tearoff, iid, ppArg,
924 : ppArgRef, vp, nsnull)))
925 0 : return NS_ERROR_XPC_BAD_CONVERT_JS;
926 111 : return NS_OK;
927 : }
928 : // else...
929 : // Slow path.
930 :
931 : // XXX E4X breaks the world. Don't try wrapping E4X objects!
932 : // This hack can be removed (or changed accordingly) when the
933 : // DOM <-> E4X bindings are complete, see bug 270553
934 4587 : if (JS_TypeOfValue(cx, OBJECT_TO_JSVAL(src)) == JSTYPE_XML) {
935 0 : *ppArgRef = nsnull;
936 0 : return NS_ERROR_XPC_BAD_CONVERT_JS;
937 : }
938 :
939 : // Try to unwrap a slim wrapper.
940 : nsISupports *iface;
941 4587 : if (XPCConvert::GetISupportsFromJSObject(src, &iface)) {
942 0 : if (!iface || NS_FAILED(iface->QueryInterface(iid, ppArg))) {
943 0 : *ppArgRef = nsnull;
944 0 : return NS_ERROR_XPC_BAD_CONVERT_JS;
945 : }
946 :
947 0 : *ppArgRef = static_cast<nsISupports*>(*ppArg);
948 0 : return NS_OK;
949 : }
950 :
951 : // Create the ccx needed for quick stubs.
952 9174 : XPCCallContext ccx(JS_CALLER, cx);
953 4587 : if (!ccx.IsValid()) {
954 0 : *ppArgRef = nsnull;
955 0 : return NS_ERROR_XPC_BAD_CONVERT_JS;
956 : }
957 :
958 9174 : nsRefPtr<nsXPCWrappedJS> wrappedJS;
959 : rv = nsXPCWrappedJS::GetNewOrUsed(ccx, src, iid, nsnull,
960 4587 : getter_AddRefs(wrappedJS));
961 4587 : if (NS_FAILED(rv) || !wrappedJS) {
962 0 : *ppArgRef = nsnull;
963 0 : return rv;
964 : }
965 :
966 : // We need to go through the QueryInterface logic to make this return
967 : // the right thing for the various 'special' interfaces; e.g.
968 : // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
969 : // there is an outer to avoid nasty recursion.
970 4587 : rv = wrappedJS->QueryInterface(iid, ppArg);
971 4587 : if (NS_SUCCEEDED(rv)) {
972 4587 : *ppArgRef = static_cast<nsISupports*>(*ppArg);
973 4587 : *vp = OBJECT_TO_JSVAL(wrappedJS->GetJSObject());
974 : }
975 4587 : return rv;
976 : }
977 :
978 : JSBool
979 0 : xpc_qsJsvalToCharStr(JSContext *cx, jsval v, JSAutoByteString *bytes)
980 : {
981 : JSString *str;
982 :
983 0 : JS_ASSERT(!bytes->ptr());
984 0 : if (JSVAL_IS_STRING(v)) {
985 0 : str = JSVAL_TO_STRING(v);
986 0 : } else if (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)) {
987 0 : return true;
988 : } else {
989 0 : if (!(str = JS_ValueToString(cx, v)))
990 0 : return false;
991 : }
992 0 : return !!bytes->encode(cx, str);
993 : }
994 :
995 : JSBool
996 0 : xpc_qsJsvalToWcharStr(JSContext *cx, jsval v, jsval *pval, const PRUnichar **pstr)
997 : {
998 : JSString *str;
999 :
1000 0 : if (JSVAL_IS_STRING(v)) {
1001 0 : str = JSVAL_TO_STRING(v);
1002 0 : } else if (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)) {
1003 0 : *pstr = NULL;
1004 0 : return true;
1005 : } else {
1006 0 : if (!(str = JS_ValueToString(cx, v)))
1007 0 : return false;
1008 0 : *pval = STRING_TO_JSVAL(str); // Root the new string.
1009 : }
1010 :
1011 0 : const jschar *chars = JS_GetStringCharsZ(cx, str);
1012 0 : if (!chars)
1013 0 : return false;
1014 :
1015 0 : *pstr = static_cast<const PRUnichar *>(chars);
1016 0 : return true;
1017 : }
1018 :
1019 : namespace xpc {
1020 :
1021 : bool
1022 15742 : StringToJsval(JSContext *cx, nsAString &str, JS::Value *rval)
1023 : {
1024 : // From the T_DOMSTRING case in XPCConvert::NativeData2JS.
1025 15742 : if (str.IsVoid()) {
1026 1128 : *rval = JSVAL_NULL;
1027 1128 : return true;
1028 : }
1029 14614 : return NonVoidStringToJsval(cx, str, rval);
1030 : }
1031 :
1032 : bool
1033 14614 : NonVoidStringToJsval(JSContext *cx, nsAString &str, JS::Value *rval)
1034 : {
1035 14614 : MOZ_ASSERT(!str.IsVoid());
1036 : nsStringBuffer* sharedBuffer;
1037 14614 : jsval jsstr = XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer);
1038 14614 : if (JSVAL_IS_NULL(jsstr))
1039 0 : return false;
1040 14614 : *rval = jsstr;
1041 14614 : if (sharedBuffer) {
1042 : // The string was shared but ReadableToJSVal didn't addref it.
1043 : // Move the ownership from str to jsstr.
1044 14574 : str.ForgetSharedBuffer();
1045 : }
1046 14614 : return true;
1047 : }
1048 :
1049 : } // namespace xpc
1050 :
1051 : JSBool
1052 0 : xpc_qsStringToJsstring(JSContext *cx, nsString &str, JSString **rval)
1053 : {
1054 : // From the T_DOMSTRING case in XPCConvert::NativeData2JS.
1055 0 : if (str.IsVoid()) {
1056 0 : *rval = nsnull;
1057 0 : return true;
1058 : }
1059 :
1060 : nsStringBuffer* sharedBuffer;
1061 0 : jsval jsstr = XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer);
1062 0 : if (JSVAL_IS_NULL(jsstr))
1063 0 : return false;
1064 0 : *rval = JSVAL_TO_STRING(jsstr);
1065 0 : if (sharedBuffer) {
1066 : // The string was shared but ReadableToJSVal didn't addref it.
1067 : // Move the ownership from str to jsstr.
1068 0 : str.ForgetSharedBuffer();
1069 : }
1070 0 : return true;
1071 : }
1072 :
1073 : JSBool
1074 23458 : xpc_qsXPCOMObjectToJsval(XPCLazyCallContext &lccx, qsObjectHelper &aHelper,
1075 : const nsIID *iid, XPCNativeInterface **iface,
1076 : jsval *rval)
1077 : {
1078 23458 : NS_PRECONDITION(iface, "Who did that and why?");
1079 :
1080 : // From the T_INTERFACE case in XPCConvert::NativeData2JS.
1081 : // This is one of the slowest things quick stubs do.
1082 :
1083 23458 : JSContext *cx = lccx.GetJSContext();
1084 :
1085 : nsresult rv;
1086 23458 : if (!XPCConvert::NativeInterface2JSObject(lccx, rval, nsnull,
1087 : aHelper, iid, iface,
1088 23458 : true, &rv)) {
1089 : // I can't tell if NativeInterface2JSObject throws JS exceptions
1090 : // or not. This is a sloppy stab at the right semantics; the
1091 : // method really ought to be fixed to behave consistently.
1092 0 : if (!JS_IsExceptionPending(cx))
1093 0 : xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
1094 0 : return false;
1095 : }
1096 :
1097 : #ifdef DEBUG
1098 23458 : JSObject* jsobj = JSVAL_TO_OBJECT(*rval);
1099 23458 : if (jsobj && !js::GetObjectParent(jsobj))
1100 0 : NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
1101 : "Why did we recreate this wrapper?");
1102 : #endif
1103 :
1104 23458 : return true;
1105 : }
1106 :
1107 : JSBool
1108 0 : xpc_qsVariantToJsval(XPCLazyCallContext &lccx,
1109 : nsIVariant *p,
1110 : jsval *rval)
1111 : {
1112 : // From the T_INTERFACE case in XPCConvert::NativeData2JS.
1113 : // Error handling is in XPCWrappedNative::CallMethod.
1114 0 : if (p) {
1115 : nsresult rv;
1116 0 : JSBool ok = XPCVariant::VariantDataToJS(lccx, p, &rv, rval);
1117 0 : if (!ok)
1118 0 : xpc_qsThrow(lccx.GetJSContext(), rv);
1119 0 : return ok;
1120 : }
1121 0 : *rval = JSVAL_NULL;
1122 0 : return true;
1123 : }
1124 :
1125 : #ifdef DEBUG
1126 : void
1127 70319 : xpc_qsAssertContextOK(JSContext *cx)
1128 : {
1129 70319 : XPCPerThreadData *thread = XPCPerThreadData::GetData(cx);
1130 70319 : XPCJSContextStack* stack = thread->GetJSContextStack();
1131 :
1132 70319 : JSContext *topJSContext = stack->Peek();
1133 :
1134 : // This is what we're actually trying to assert here.
1135 70319 : NS_ASSERTION(cx == topJSContext, "wrong context on XPCJSContextStack!");
1136 :
1137 70319 : NS_ASSERTION(XPCPerThreadData::IsMainThread(cx),
1138 : "XPConnect quick stub called on non-main thread");
1139 70319 : }
1140 : #endif
|