CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / ipc / ObjectWrapperChild.cpp
blobb81d6acefca470fd8f5113f541b558c21e456eea
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=80:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is mozilla.org code.
19 * The Initial Developer of the Original Code is
20 * The Mozilla Foundation.
21 * Portions created by the Initial Developer are Copyright (C) 2010
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Ben Newman <b{enjam,newma}n@mozilla.com> (original author)
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "base/basictypes.h"
42 #include "jscntxt.h"
44 #include "mozilla/jsipc/ContextWrapperChild.h"
45 #include "mozilla/jsipc/ObjectWrapperChild.h"
46 #include "mozilla/jsipc/CPOWTypes.h"
48 #include "jsapi.h"
49 #include "nsAutoPtr.h"
50 #include "nsTArray.h"
51 #include "nsContentUtils.h"
52 #include "nsIJSContextStack.h"
53 #include "nsJSUtils.h"
55 using namespace mozilla::jsipc;
56 using namespace js;
58 namespace {
60 class AutoContextPusher {
62 nsCxPusher mStack;
63 JSAutoRequest mRequest;
64 JSContext* const mContext;
65 const uint32 mSavedOptions;
66 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
68 public:
70 AutoContextPusher(JSContext* cx
71 JS_GUARD_OBJECT_NOTIFIER_PARAM)
72 : mRequest(cx)
73 , mContext(cx)
74 , mSavedOptions(JS_SetOptions(cx, (JS_GetOptions(cx) |
75 JSOPTION_DONT_REPORT_UNCAUGHT)))
77 JS_GUARD_OBJECT_NOTIFIER_INIT;
78 mStack.Push(cx, PR_FALSE);
81 ~AutoContextPusher() {
82 mStack.Pop();
83 JS_SetOptions(mContext, mSavedOptions);
88 class StatusPtrOwner
90 OperationStatus* mStatusPtr;
91 public:
92 StatusPtrOwner() : mStatusPtr(NULL) {}
93 void SetStatusPtr(OperationStatus* statusPtr) {
94 mStatusPtr = statusPtr;
95 // By default, initialize mStatusPtr to failure without an
96 // exception. Doing so only when the union is uninitialized
97 // allows AutoCheckOperation classes to be nested on the
98 // stack, just in case AnswerConstruct, for example, calls
99 // AnswerCall (as it once did, before there were unrelated
100 // problems with that approach).
101 if (mStatusPtr->type() == OperationStatus::T__None)
102 *mStatusPtr = JS_FALSE;
104 OperationStatus* StatusPtr() {
105 NS_ASSERTION(mStatusPtr, "Should have called SetStatusPtr by now.");
106 return mStatusPtr;
110 typedef AutoCheckOperationBase<StatusPtrOwner> ACOBase;
112 class AutoCheckOperation : public ACOBase
114 JS_DECL_USE_GUARD_OBJECT_NOTIFIER
115 public:
116 AutoCheckOperation(ObjectWrapperChild* owc,
117 OperationStatus* statusPtr
118 JS_GUARD_OBJECT_NOTIFIER_PARAM)
119 : ACOBase(NULL, owc)
121 JS_GUARD_OBJECT_NOTIFIER_INIT;
122 SetStatusPtr(statusPtr);
127 void
128 ObjectWrapperChild::CheckOperation(JSContext*,
129 OperationStatus* status)
131 NS_PRECONDITION(status->type() != OperationStatus::T__None,
132 "Checking an uninitialized operation.");
134 JSContext* cx = Manager()->GetContext();
135 jsval thrown;
137 if (JS_GetPendingException(cx, &thrown)) {
138 NS_ASSERTION(!(status->type() == OperationStatus::TJSBool &&
139 status->get_JSBool()),
140 "Operation succeeded but exception was thrown?");
141 JSVariant exception;
142 if (!jsval_to_JSVariant(cx, thrown, &exception))
143 exception = void_t(); // XXX Useful?
144 *status = exception;
145 JS_ClearPendingException(cx);
149 ObjectWrapperChild::ObjectWrapperChild(JSContext* cx, JSObject* obj)
150 : mObj(obj)
152 JSAutoRequest request(cx);
153 #ifdef DEBUG
154 bool added =
155 #endif
156 JS_AddObjectRoot(cx, &mObj);
157 NS_ASSERTION(added, "ObjectWrapperChild constructor failed to root JSObject*");
160 void
161 ObjectWrapperChild::ActorDestroy(ActorDestroyReason why)
163 JSContext* cx = Manager()->GetContext();
164 JSAutoRequest request(cx);
165 JS_RemoveObjectRoot(cx, &mObj);
168 bool
169 ObjectWrapperChild::JSObject_to_JSVariant(JSContext* cx, JSObject* from,
170 JSVariant* to)
172 *to = Manager()->GetOrCreateWrapper(from);
173 return true;
176 bool
177 ObjectWrapperChild::jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to)
179 switch (JS_TypeOfValue(cx, from)) {
180 case JSTYPE_VOID:
181 *to = void_t();
182 return true;
183 case JSTYPE_NULL:
184 if (from != JSVAL_NULL)
185 return false;
186 // fall through
187 case JSTYPE_FUNCTION:
188 // fall through
189 case JSTYPE_OBJECT:
190 return JSObject_to_JSVariant(cx, JSVAL_TO_OBJECT(from), to);
191 case JSTYPE_STRING:
193 nsDependentJSString depStr;
194 if (!depStr.init(cx, from))
195 return false;
196 *to = depStr;
198 return true;
199 case JSTYPE_NUMBER:
200 if (JSVAL_IS_INT(from))
201 *to = JSVAL_TO_INT(from);
202 else if (JSVAL_IS_DOUBLE(from))
203 *to = JSVAL_TO_DOUBLE(from);
204 else return false;
205 return true;
206 case JSTYPE_BOOLEAN:
207 *to = !!JSVAL_TO_BOOLEAN(from);
208 return true;
209 case JSTYPE_XML:
210 // fall through
211 default:
212 return false;
216 /*static*/ bool
217 ObjectWrapperChild::
218 JSObject_from_PObjectWrapperChild(JSContext*,
219 const PObjectWrapperChild* from,
220 JSObject** to)
222 const ObjectWrapperChild* owc =
223 static_cast<const ObjectWrapperChild*>(from);
224 *to = owc ? owc->mObj : NULL;
225 return true;
228 /*static*/ bool
229 ObjectWrapperChild::JSObject_from_JSVariant(JSContext* cx,
230 const JSVariant& from,
231 JSObject** to)
233 if (from.type() != JSVariant::TPObjectWrapperChild)
234 return false;
235 return JSObject_from_PObjectWrapperChild(cx,
236 from.get_PObjectWrapperChild(),
237 to);
240 /*static*/ bool
241 ObjectWrapperChild::jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
242 jsval* to)
244 switch (from.type()) {
245 case JSVariant::Tvoid_t:
246 *to = JSVAL_VOID;
247 return true;
248 case JSVariant::TPObjectWrapperChild:
250 JSObject* obj;
251 if (!JSObject_from_JSVariant(cx, from, &obj))
252 return false;
253 *to = OBJECT_TO_JSVAL(obj);
254 return true;
256 case JSVariant::TnsString:
258 const nsString& str = from.get_nsString();
259 JSString* s = JS_NewUCStringCopyN(cx,
260 str.BeginReading(),
261 str.Length());
262 if (!s)
263 return false;
264 *to = STRING_TO_JSVAL(s);
266 return true;
267 case JSVariant::Tint:
268 *to = INT_TO_JSVAL(from.get_int());
269 return true;
270 case JSVariant::Tdouble:
271 return !!JS_NewNumberValue(cx, from.get_double(), to);
272 case JSVariant::Tbool:
273 *to = BOOLEAN_TO_JSVAL(from.get_bool());
274 return true;
275 default:
276 return false;
280 ContextWrapperChild*
281 ObjectWrapperChild::Manager()
283 PContextWrapperChild* pcwc = PObjectWrapperChild::Manager();
284 return static_cast<ContextWrapperChild*>(pcwc);
287 static bool
288 jsid_to_nsString(JSContext* cx, jsid from, nsString* to)
290 if (JSID_IS_STRING(from)) {
291 size_t length;
292 const jschar* chars = JS_GetInternedStringCharsAndLength(JSID_TO_STRING(from), &length);
293 *to = nsDependentString(chars, length);
294 return true;
296 return false;
299 static bool
300 jsid_from_nsString(JSContext* cx, const nsString& from, jsid* to)
302 JSString* str = JS_NewUCStringCopyN(cx, from.BeginReading(), from.Length());
303 if (!str)
304 return false;
305 return JS_ValueToId(cx, STRING_TO_JSVAL(str), to);
308 #if 0
309 // The general schema for ObjectWrapperChild::Answer* methods:
310 bool
311 ObjectWrapperChild::AnswerSomething(/* in-parameters */
312 /* out-parameters */)
314 // initialize out-parameters for failure
315 JSContext* cx = Manager()->GetContext();
316 AutoContextPusher acp(cx);
317 // validate in-parameters, else return false
318 // successfully perform local JS operations, else return true
319 // perform out-parameter conversions, else return false
320 return true;
322 // There's an important subtlety here: though a local JS operation may
323 // fail, leaving out-parameters uninitialized, we must initialize all
324 // out-parameters when reporting success (returning true) to the IPC
325 // messaging system. See AnswerGetProperty for illustration.
326 #endif
328 bool
329 ObjectWrapperChild::AnswerAddProperty(const nsString& id,
330 OperationStatus* status)
332 jsid interned_id;
334 JSContext* cx = Manager()->GetContext();
335 AutoContextPusher acp(cx);
336 AutoCheckOperation aco(this, status);
338 if (!jsid_from_nsString(cx, id, &interned_id))
339 return false;
341 *status = JS_DefinePropertyById(cx, mObj, interned_id, JSVAL_VOID,
342 NULL, NULL, 0);
343 return true;
346 bool
347 ObjectWrapperChild::AnswerGetProperty(const nsString& id,
348 OperationStatus* status, JSVariant* vp)
350 jsid interned_id;
351 jsval val;
353 JSContext* cx = Manager()->GetContext();
354 AutoContextPusher acp(cx);
355 AutoCheckOperation aco(this, status);
357 if (!jsid_from_nsString(cx, id, &interned_id))
358 return false;
360 *status = JS_GetPropertyById(cx, mObj, interned_id, &val);
362 // Since we fully expect this call to jsval_to_JSVariant to return
363 // true, we can't just leave vp uninitialized when JS_GetPropertyById
364 // returns JS_FALSE. This pitfall could be avoided in general if IPDL
365 // ensured that outparams were pre-initialized to some default value
366 // (XXXfixme cjones?).
367 return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp);
370 bool
371 ObjectWrapperChild::AnswerSetProperty(const nsString& id, const JSVariant& v,
372 OperationStatus* status, JSVariant* vp)
374 jsid interned_id;
375 jsval val;
377 *vp = v;
379 JSContext* cx = Manager()->GetContext();
380 AutoContextPusher acp(cx);
381 AutoCheckOperation aco(this, status);
383 if (!jsid_from_nsString(cx, id, &interned_id) ||
384 !jsval_from_JSVariant(cx, v, &val))
385 return false;
387 *status = JS_SetPropertyById(cx, mObj, interned_id, &val);
389 return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp);
392 bool
393 ObjectWrapperChild::AnswerDelProperty(const nsString& id,
394 OperationStatus* status, JSVariant* vp)
396 jsid interned_id;
397 jsval val;
399 JSContext* cx = Manager()->GetContext();
400 AutoContextPusher acp(cx);
401 AutoCheckOperation aco(this, status);
403 if (!jsid_from_nsString(cx, id, &interned_id))
404 return false;
406 *status = JS_DeletePropertyById2(cx, mObj, interned_id, &val);
408 return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp);
411 static const PRUint32 sNextIdIndexSlot = 0;
412 static const PRUint32 sNumNewEnumerateStateSlots = 1;
414 static void
415 CPOW_NewEnumerateState_Finalize(JSContext* cx, JSObject* state)
417 nsTArray<nsString>* strIds =
418 static_cast<nsTArray<nsString>*>(JS_GetPrivate(cx, state));
420 if (strIds) {
421 delete strIds;
422 JS_SetPrivate(cx, state, NULL);
426 // Similar to IteratorClass in XPCWrapper.cpp
427 static const JSClass sCPOW_NewEnumerateState_JSClass = {
428 "CPOW NewEnumerate State",
429 JSCLASS_HAS_PRIVATE |
430 JSCLASS_HAS_RESERVED_SLOTS(sNumNewEnumerateStateSlots),
431 JS_PropertyStub, JS_PropertyStub,
432 JS_PropertyStub, JS_StrictPropertyStub,
433 JS_EnumerateStub, JS_ResolveStub,
434 JS_ConvertStub, CPOW_NewEnumerateState_Finalize,
435 JSCLASS_NO_OPTIONAL_MEMBERS
438 bool
439 ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */
440 OperationStatus* status, JSVariant* statep, int* idp)
442 *idp = 0;
444 JSContext* cx = Manager()->GetContext();
445 AutoContextPusher acp(cx);
446 AutoCheckOperation aco(this, status);
448 JSClass* clasp = const_cast<JSClass*>(&sCPOW_NewEnumerateState_JSClass);
449 JSObject* state = JS_NewObjectWithGivenProto(cx, clasp, NULL, NULL);
450 if (!state)
451 return false;
452 AutoObjectRooter tvr(cx, state);
454 for (JSObject* proto = mObj;
455 proto;
456 proto = JS_GetPrototype(cx, proto))
458 AutoIdArray ids(cx, JS_Enumerate(cx, proto));
459 for (uint i = 0; i < ids.length(); ++i)
460 JS_DefinePropertyById(cx, state, ids[i], JSVAL_VOID,
461 NULL, NULL, JSPROP_ENUMERATE | JSPROP_SHARED);
464 InfallibleTArray<nsString>* strIds;
466 AutoIdArray ids(cx, JS_Enumerate(cx, state));
467 if (!ids)
468 return false;
469 strIds = new InfallibleTArray<nsString>(ids.length());
470 for (uint i = 0; i < ids.length(); ++i)
471 if (!jsid_to_nsString(cx, ids[i], strIds->AppendElement())) {
472 delete strIds;
473 return false;
476 *idp = strIds->Length();
478 *status = (JS_SetPrivate(cx, state, strIds) &&
479 JS_SetReservedSlot(cx, state, sNextIdIndexSlot,
480 JSVAL_ZERO) &&
481 JSObject_to_JSVariant(cx, state, statep));
483 return true;
486 bool
487 ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state,
488 OperationStatus* status, JSVariant* statep, nsString* idp)
490 JSObject* state;
491 jsval v;
493 *statep = in_state;
494 idp->Truncate();
496 JSContext* cx = Manager()->GetContext();
497 AutoContextPusher acp(cx);
498 AutoCheckOperation aco(this, status);
500 if (!JSObject_from_JSVariant(cx, in_state, &state))
501 return false;
503 InfallibleTArray<nsString>* strIds =
504 static_cast<InfallibleTArray<nsString>*>(JS_GetPrivate(cx, state));
506 if (!strIds || !JS_GetReservedSlot(cx, state, sNextIdIndexSlot, &v))
507 return false;
509 jsuint i = JSVAL_TO_INT(v);
510 NS_ASSERTION(i >= 0, "Index of next jsid negative?");
511 NS_ASSERTION(i <= strIds->Length(), "Index of next jsid too large?");
513 if (jsuint(i) == strIds->Length()) {
514 *status = JS_TRUE;
515 return JSObject_to_JSVariant(cx, NULL, statep);
518 *idp = strIds->ElementAt(i);
519 *status = JS_SetReservedSlot(cx, state, sNextIdIndexSlot,
520 INT_TO_JSVAL(i + 1));
521 return true;
524 bool
525 ObjectWrapperChild::RecvNewEnumerateDestroy(const JSVariant& in_state)
527 JSObject* state;
529 JSContext* cx = Manager()->GetContext();
530 AutoContextPusher acp(cx);
532 if (!JSObject_from_JSVariant(cx, in_state, &state))
533 return false;
535 CPOW_NewEnumerateState_Finalize(cx, state);
537 return true;
540 bool
541 ObjectWrapperChild::AnswerNewResolve(const nsString& id, const int& flags,
542 OperationStatus* status, PObjectWrapperChild** obj2)
544 jsid interned_id;
546 *obj2 = NULL;
548 JSContext* cx = Manager()->GetContext();
549 AutoContextPusher acp(cx);
550 AutoCheckOperation aco(this, status);
552 if (!jsid_from_nsString(cx, id, &interned_id))
553 return false;
555 CPOW_LOG(("new-resolving \"%s\"...",
556 NS_ConvertUTF16toUTF8(id).get()));
558 JSPropertyDescriptor desc;
559 if (!JS_GetPropertyDescriptorById(cx, mObj, interned_id, flags, &desc))
560 return true;
562 *status = JS_TRUE;
564 if (desc.obj)
565 *obj2 = Manager()->GetOrCreateWrapper(desc.obj);
567 return true;
570 bool
571 ObjectWrapperChild::AnswerConvert(const JSType& type,
572 OperationStatus* status, JSVariant* vp)
574 jsval v;
575 JSContext* cx = Manager()->GetContext();
576 AutoContextPusher acp(cx);
577 AutoCheckOperation aco(this, status);
578 *status = JS_ConvertValue(cx, OBJECT_TO_JSVAL(mObj), type, &v);
579 return jsval_to_JSVariant(cx, aco.Ok() ? v : JSVAL_VOID, vp);
582 namespace {
583 // Should be an overestimate of typical JS function arity.
584 typedef nsAutoTArray<jsval, 5> AutoJSArgs;
587 bool
588 ObjectWrapperChild::AnswerCall(PObjectWrapperChild* receiver, const InfallibleTArray<JSVariant>& argv,
589 OperationStatus* status, JSVariant* rval)
591 JSContext* cx = Manager()->GetContext();
592 AutoContextPusher acp(cx);
593 AutoCheckOperation aco(this, status);
595 JSObject* obj;
596 if (!JSObject_from_PObjectWrapperChild(cx, receiver, &obj))
597 return false;
599 AutoJSArgs args;
600 PRUint32 argc = argv.Length();
601 jsval *jsargs = args.AppendElements(argc);
602 if (!jsargs)
603 return false;
604 AutoArrayRooter tvr(cx, argc, jsargs);
606 for (PRUint32 i = 0; i < argc; ++i)
607 if (!jsval_from_JSVariant(cx, argv.ElementAt(i), jsargs + i))
608 return false;
610 jsval rv;
611 *status = JS_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(mObj),
612 argv.Length(), jsargs, &rv);
614 return jsval_to_JSVariant(cx, aco.Ok() ? rv : JSVAL_VOID, rval);
617 bool
618 ObjectWrapperChild::AnswerConstruct(const InfallibleTArray<JSVariant>& argv,
619 OperationStatus* status, PObjectWrapperChild** rval)
621 JSContext* cx = Manager()->GetContext();
622 AutoContextPusher acp(cx);
623 AutoCheckOperation aco(this, status);
625 AutoJSArgs args;
626 PRUint32 argc = argv.Length();
627 jsval* jsargs = args.AppendElements(argc);
628 if (!jsargs)
629 return false;
630 AutoArrayRooter tvr(cx, argc, jsargs);
632 for (PRUint32 i = 0; i < argc; ++i)
633 if (!jsval_from_JSVariant(cx, argv.ElementAt(i), jsargs + i))
634 return false;
636 JSObject* obj = JS_New(cx, mObj, argc, jsargs);
638 *status = !!obj;
639 *rval = Manager()->GetOrCreateWrapper(obj);
641 return true;
644 bool
645 ObjectWrapperChild::AnswerHasInstance(const JSVariant& v,
646 OperationStatus* status, JSBool* bp)
648 jsval candidate;
649 JSContext* cx = Manager()->GetContext();
650 AutoContextPusher acp(cx);
651 AutoCheckOperation aco(this, status);
652 if (!jsval_from_JSVariant(cx, v, &candidate))
653 return false;
654 *status = JS_HasInstance(cx, mObj, candidate, bp);
655 return true;