Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 jscntxtinlines_h
8 #define jscntxtinlines_h
10 #include "jscntxt.h"
11 #include "jscompartment.h"
13 #include "jsiter.h"
14 #include "jsworkers.h"
16 #include "builtin/Object.h"
17 #include "jit/IonFrames.h"
18 #include "vm/ForkJoin.h"
19 #include "vm/Interpreter.h"
20 #include "vm/ProxyObject.h"
22 namespace js {
24 #ifdef JS_CRASH_DIAGNOSTICS
25 class CompartmentChecker
26 {
27 JSCompartment *compartment;
29 public:
30 explicit CompartmentChecker(ExclusiveContext *cx)
31 : compartment(cx->compartment())
32 {
33 #ifdef DEBUG
34 // In debug builds, make sure the embedder passed the cx it claimed it
35 // was going to use.
36 JSContext *activeContext = nullptr;
37 if (cx->isJSContext())
38 activeContext = cx->asJSContext()->runtime()->activeContext;
39 JS_ASSERT_IF(activeContext, cx == activeContext);
40 #endif
41 }
43 /*
44 * Set a breakpoint here (break js::CompartmentChecker::fail) to debug
45 * compartment mismatches.
46 */
47 static void fail(JSCompartment *c1, JSCompartment *c2) {
48 printf("*** Compartment mismatch %p vs. %p\n", (void *) c1, (void *) c2);
49 MOZ_CRASH();
50 }
52 static void fail(JS::Zone *z1, JS::Zone *z2) {
53 printf("*** Zone mismatch %p vs. %p\n", (void *) z1, (void *) z2);
54 MOZ_CRASH();
55 }
57 /* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */
58 static void check(JSCompartment *c1, JSCompartment *c2) {
59 JS_ASSERT(!c1->runtimeFromAnyThread()->isAtomsCompartment(c1));
60 JS_ASSERT(!c2->runtimeFromAnyThread()->isAtomsCompartment(c2));
61 if (c1 != c2)
62 fail(c1, c2);
63 }
65 void check(JSCompartment *c) {
66 if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) {
67 if (!compartment)
68 compartment = c;
69 else if (c != compartment)
70 fail(compartment, c);
71 }
72 }
74 void checkZone(JS::Zone *z) {
75 if (compartment && z != compartment->zone())
76 fail(compartment->zone(), z);
77 }
79 void check(JSObject *obj) {
80 if (obj)
81 check(obj->compartment());
82 }
84 template<typename T>
85 void check(const Rooted<T>& rooted) {
86 check(rooted.get());
87 }
89 template<typename T>
90 void check(Handle<T> handle) {
91 check(handle.get());
92 }
94 void check(JSString *str) {
95 if (!str->isAtom())
96 checkZone(str->zone());
97 }
99 void check(const js::Value &v) {
100 if (v.isObject())
101 check(&v.toObject());
102 else if (v.isString())
103 check(v.toString());
104 }
106 void check(const ValueArray &arr) {
107 for (size_t i = 0; i < arr.length; i++)
108 check(arr.array[i]);
109 }
111 void check(const JSValueArray &arr) {
112 for (size_t i = 0; i < arr.length; i++)
113 check(arr.array[i]);
114 }
116 void check(const JS::HandleValueArray &arr) {
117 for (size_t i = 0; i < arr.length(); i++)
118 check(arr[i]);
119 }
121 void check(const CallArgs &args) {
122 for (Value *p = args.base(); p != args.end(); ++p)
123 check(*p);
124 }
126 void check(jsid id) {
127 if (JSID_IS_OBJECT(id))
128 check(JSID_TO_OBJECT(id));
129 }
131 void check(JSIdArray *ida) {
132 if (ida) {
133 for (int i = 0; i < ida->length; i++) {
134 if (JSID_IS_OBJECT(ida->vector[i]))
135 check(ida->vector[i]);
136 }
137 }
138 }
140 void check(JSScript *script) {
141 if (script)
142 check(script->compartment());
143 }
145 void check(InterpreterFrame *fp);
146 void check(AbstractFramePtr frame);
147 };
148 #endif /* JS_CRASH_DIAGNOSTICS */
150 /*
151 * Don't perform these checks when called from a finalizer. The checking
152 * depends on other objects not having been swept yet.
153 */
154 #define START_ASSERT_SAME_COMPARTMENT() \
155 if (!cx->isExclusiveContext()) \
156 return; \
157 if (cx->isJSContext() && cx->asJSContext()->runtime()->isHeapBusy()) \
158 return; \
159 CompartmentChecker c(cx->asExclusiveContext())
161 template <class T1> inline void
162 assertSameCompartment(ThreadSafeContext *cx, const T1 &t1)
163 {
164 #ifdef JS_CRASH_DIAGNOSTICS
165 START_ASSERT_SAME_COMPARTMENT();
166 c.check(t1);
167 #endif
168 }
170 template <class T1> inline void
171 assertSameCompartmentDebugOnly(ThreadSafeContext *cx, const T1 &t1)
172 {
173 #ifdef DEBUG
174 START_ASSERT_SAME_COMPARTMENT();
175 c.check(t1);
176 #endif
177 }
179 template <class T1, class T2> inline void
180 assertSameCompartment(ThreadSafeContext *cx, const T1 &t1, const T2 &t2)
181 {
182 #ifdef JS_CRASH_DIAGNOSTICS
183 START_ASSERT_SAME_COMPARTMENT();
184 c.check(t1);
185 c.check(t2);
186 #endif
187 }
189 template <class T1, class T2, class T3> inline void
190 assertSameCompartment(ThreadSafeContext *cx, const T1 &t1, const T2 &t2, const T3 &t3)
191 {
192 #ifdef JS_CRASH_DIAGNOSTICS
193 START_ASSERT_SAME_COMPARTMENT();
194 c.check(t1);
195 c.check(t2);
196 c.check(t3);
197 #endif
198 }
200 template <class T1, class T2, class T3, class T4> inline void
201 assertSameCompartment(ThreadSafeContext *cx,
202 const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4)
203 {
204 #ifdef JS_CRASH_DIAGNOSTICS
205 START_ASSERT_SAME_COMPARTMENT();
206 c.check(t1);
207 c.check(t2);
208 c.check(t3);
209 c.check(t4);
210 #endif
211 }
213 template <class T1, class T2, class T3, class T4, class T5> inline void
214 assertSameCompartment(ThreadSafeContext *cx,
215 const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5)
216 {
217 #ifdef JS_CRASH_DIAGNOSTICS
218 START_ASSERT_SAME_COMPARTMENT();
219 c.check(t1);
220 c.check(t2);
221 c.check(t3);
222 c.check(t4);
223 c.check(t5);
224 #endif
225 }
227 #undef START_ASSERT_SAME_COMPARTMENT
229 STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
230 MOZ_ALWAYS_INLINE bool
231 CallJSNative(JSContext *cx, Native native, const CallArgs &args)
232 {
233 JS_CHECK_RECURSION(cx, return false);
235 #ifdef DEBUG
236 bool alreadyThrowing = cx->isExceptionPending();
237 #endif
238 assertSameCompartment(cx, args);
239 bool ok = native(cx, args.length(), args.base());
240 if (ok) {
241 assertSameCompartment(cx, args.rval());
242 JS_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
243 }
244 return ok;
245 }
247 STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
248 MOZ_ALWAYS_INLINE bool
249 CallNativeImpl(JSContext *cx, NativeImpl impl, const CallArgs &args)
250 {
251 #ifdef DEBUG
252 bool alreadyThrowing = cx->isExceptionPending();
253 #endif
254 assertSameCompartment(cx, args);
255 bool ok = impl(cx, args);
256 if (ok) {
257 assertSameCompartment(cx, args.rval());
258 JS_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
259 }
260 return ok;
261 }
263 STATIC_PRECONDITION(ubound(args.argv_) >= argc)
264 MOZ_ALWAYS_INLINE bool
265 CallJSNativeConstructor(JSContext *cx, Native native, const CallArgs &args)
266 {
267 #ifdef DEBUG
268 RootedObject callee(cx, &args.callee());
269 #endif
271 JS_ASSERT(args.thisv().isMagic());
272 if (!CallJSNative(cx, native, args))
273 return false;
275 /*
276 * Native constructors must return non-primitive values on success.
277 * Although it is legal, if a constructor returns the callee, there is a
278 * 99.9999% chance it is a bug. If any valid code actually wants the
279 * constructor to return the callee, the assertion can be removed or
280 * (another) conjunct can be added to the antecedent.
281 *
282 * Exceptions:
283 *
284 * - Proxies are exceptions to both rules: they can return primitives and
285 * they allow content to return the callee.
286 *
287 * - CallOrConstructBoundFunction is an exception as well because we might
288 * have used bind on a proxy function.
289 *
290 * - new Iterator(x) is user-hookable; it returns x.__iterator__() which
291 * could be any object.
292 *
293 * - (new Object(Object)) returns the callee.
294 */
295 JS_ASSERT_IF(native != ProxyObject::callableClass_.construct &&
296 native != js::CallOrConstructBoundFunction &&
297 native != js::IteratorConstructor &&
298 (!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct),
299 !args.rval().isPrimitive() && callee != &args.rval().toObject());
301 return true;
302 }
304 MOZ_ALWAYS_INLINE bool
305 CallJSPropertyOp(JSContext *cx, PropertyOp op, HandleObject receiver, HandleId id, MutableHandleValue vp)
306 {
307 JS_CHECK_RECURSION(cx, return false);
309 assertSameCompartment(cx, receiver, id, vp);
310 bool ok = op(cx, receiver, id, vp);
311 if (ok)
312 assertSameCompartment(cx, vp);
313 return ok;
314 }
316 MOZ_ALWAYS_INLINE bool
317 CallJSPropertyOpSetter(JSContext *cx, StrictPropertyOp op, HandleObject obj, HandleId id,
318 bool strict, MutableHandleValue vp)
319 {
320 JS_CHECK_RECURSION(cx, return false);
322 assertSameCompartment(cx, obj, id, vp);
323 return op(cx, obj, id, strict, vp);
324 }
326 static inline bool
327 CallJSDeletePropertyOp(JSContext *cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id,
328 bool *succeeded)
329 {
330 JS_CHECK_RECURSION(cx, return false);
332 assertSameCompartment(cx, receiver, id);
333 return op(cx, receiver, id, succeeded);
334 }
336 inline bool
337 CallSetter(JSContext *cx, HandleObject obj, HandleId id, StrictPropertyOp op, unsigned attrs,
338 bool strict, MutableHandleValue vp)
339 {
340 if (attrs & JSPROP_SETTER) {
341 RootedValue opv(cx, CastAsObjectJsval(op));
342 return InvokeGetterOrSetter(cx, obj, opv, 1, vp.address(), vp);
343 }
345 if (attrs & JSPROP_GETTER)
346 return js_ReportGetterOnlyAssignment(cx, strict);
348 return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp);
349 }
351 inline uintptr_t
352 GetNativeStackLimit(ThreadSafeContext *cx)
353 {
354 StackKind kind;
355 if (cx->isJSContext()) {
356 kind = cx->asJSContext()->runningWithTrustedPrincipals()
357 ? StackForTrustedScript : StackForUntrustedScript;
358 } else {
359 // For other threads, we just use the trusted stack depth, since it's
360 // unlikely that we'll be mixing trusted and untrusted code together.
361 kind = StackForTrustedScript;
362 }
363 return cx->perThreadData->nativeStackLimit[kind];
364 }
366 inline LifoAlloc &
367 ExclusiveContext::typeLifoAlloc()
368 {
369 return zone()->types.typeLifoAlloc;
370 }
372 } /* namespace js */
374 inline void
375 JSContext::setPendingException(js::Value v)
376 {
377 JS_ASSERT(!IsPoisonedValue(v));
378 this->throwing = true;
379 this->unwrappedException_ = v;
380 // We don't use assertSameCompartment here to allow
381 // js::SetPendingExceptionCrossContext to work.
382 JS_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());
383 }
385 inline void
386 JSContext::setDefaultCompartmentObject(JSObject *obj)
387 {
388 JS_ASSERT(!options().noDefaultCompartmentObject());
389 defaultCompartmentObject_ = obj;
390 }
392 inline void
393 JSContext::setDefaultCompartmentObjectIfUnset(JSObject *obj)
394 {
395 if (!options().noDefaultCompartmentObject() &&
396 !defaultCompartmentObject_)
397 {
398 setDefaultCompartmentObject(obj);
399 }
400 }
402 inline void
403 js::ExclusiveContext::enterCompartment(JSCompartment *c)
404 {
405 enterCompartmentDepth_++;
406 c->enter();
407 setCompartment(c);
408 }
410 inline void
411 js::ExclusiveContext::enterNullCompartment()
412 {
413 enterCompartmentDepth_++;
414 setCompartment(nullptr);
415 }
417 inline void
418 js::ExclusiveContext::leaveCompartment(JSCompartment *oldCompartment)
419 {
420 JS_ASSERT(hasEnteredCompartment());
421 enterCompartmentDepth_--;
423 // Only call leave() after we've setCompartment()-ed away from the current
424 // compartment.
425 JSCompartment *startingCompartment = compartment_;
426 setCompartment(oldCompartment);
427 if (startingCompartment)
428 startingCompartment->leave();
429 }
431 inline void
432 js::ExclusiveContext::setCompartment(JSCompartment *comp)
433 {
434 // ExclusiveContexts can only be in the atoms zone or in exclusive zones.
435 JS_ASSERT_IF(!isJSContext() && !runtime_->isAtomsCompartment(comp),
436 comp->zone()->usedByExclusiveThread);
438 // Normal JSContexts cannot enter exclusive zones.
439 JS_ASSERT_IF(isJSContext() && comp,
440 !comp->zone()->usedByExclusiveThread);
442 // Only one thread can be in the atoms compartment at a time.
443 JS_ASSERT_IF(runtime_->isAtomsCompartment(comp),
444 runtime_->currentThreadHasExclusiveAccess());
446 // Make sure that the atoms compartment has its own zone.
447 JS_ASSERT_IF(comp && !runtime_->isAtomsCompartment(comp),
448 !runtime_->isAtomsZone(comp->zone()));
450 // Both the current and the new compartment should be properly marked as
451 // entered at this point.
452 JS_ASSERT_IF(compartment_, compartment_->hasBeenEntered());
453 JS_ASSERT_IF(comp, comp->hasBeenEntered());
455 compartment_ = comp;
456 zone_ = comp ? comp->zone() : nullptr;
457 allocator_ = zone_ ? &zone_->allocator : nullptr;
458 }
460 inline JSScript *
461 JSContext::currentScript(jsbytecode **ppc,
462 MaybeAllowCrossCompartment allowCrossCompartment) const
463 {
464 if (ppc)
465 *ppc = nullptr;
467 js::Activation *act = mainThread().activation();
468 while (act && (act->cx() != this || (act->isJit() && !act->asJit()->isActive())))
469 act = act->prev();
471 if (!act)
472 return nullptr;
474 JS_ASSERT(act->cx() == this);
476 #ifdef JS_ION
477 if (act->isJit()) {
478 JSScript *script = nullptr;
479 js::jit::GetPcScript(const_cast<JSContext *>(this), &script, ppc);
480 if (!allowCrossCompartment && script->compartment() != compartment())
481 return nullptr;
482 return script;
483 }
485 if (act->isAsmJS())
486 return nullptr;
487 #endif
489 JS_ASSERT(act->isInterpreter());
491 js::InterpreterFrame *fp = act->asInterpreter()->current();
492 JS_ASSERT(!fp->runningInJit());
494 JSScript *script = fp->script();
495 if (!allowCrossCompartment && script->compartment() != compartment())
496 return nullptr;
498 if (ppc) {
499 *ppc = act->asInterpreter()->regs().pc;
500 JS_ASSERT(script->containsPC(*ppc));
501 }
502 return script;
503 }
505 template <JSThreadSafeNative threadSafeNative>
506 inline bool
507 JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp)
508 {
509 return threadSafeNative(cx, argc, vp);
510 }
512 template <JSThreadSafeNative threadSafeNative>
513 inline bool
514 JSParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp)
515 {
516 return threadSafeNative(cx, argc, vp);
517 }
519 /* static */ inline JSContext *
520 js::ExecutionModeTraits<js::SequentialExecution>::toContextType(ExclusiveContext *cx)
521 {
522 return cx->asJSContext();
523 }
525 #endif /* jscntxtinlines_h */