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
11 #include "jscompartment.h"
15 #include "builtin/Object.h"
16 #include "jit/JitFrames.h"
17 #include "vm/ForkJoin.h"
18 #include "vm/HelperThreads.h"
19 #include "vm/Interpreter.h"
20 #include "vm/ProxyObject.h"
21 #include "vm/Symbol.h"
25 #ifdef JS_CRASH_DIAGNOSTICS
26 class CompartmentChecker
28 JSCompartment
* compartment
;
31 explicit CompartmentChecker(ExclusiveContext
* cx
)
32 : compartment(cx
->compartment())
35 // In debug builds, make sure the embedder passed the cx it claimed it
37 JSContext
* activeContext
= nullptr;
38 if (cx
->isJSContext())
39 activeContext
= cx
->asJSContext()->runtime()->activeContext
;
40 MOZ_ASSERT_IF(activeContext
, cx
== activeContext
);
45 * Set a breakpoint here (break js::CompartmentChecker::fail) to debug
46 * compartment mismatches.
48 static void fail(JSCompartment
* c1
, JSCompartment
* c2
) {
49 printf("*** Compartment mismatch %p vs. %p\n", (void*) c1
, (void*) c2
);
53 static void fail(JS::Zone
* z1
, JS::Zone
* z2
) {
54 printf("*** Zone mismatch %p vs. %p\n", (void*) z1
, (void*) z2
);
58 /* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */
59 static void check(JSCompartment
* c1
, JSCompartment
* c2
) {
60 MOZ_ASSERT(!c1
->runtimeFromAnyThread()->isAtomsCompartment(c1
));
61 MOZ_ASSERT(!c2
->runtimeFromAnyThread()->isAtomsCompartment(c2
));
66 void check(JSCompartment
* c
) {
67 if (c
&& !compartment
->runtimeFromAnyThread()->isAtomsCompartment(c
)) {
70 else if (c
!= compartment
)
75 void checkZone(JS::Zone
* z
) {
76 if (compartment
&& z
!= compartment
->zone())
77 fail(compartment
->zone(), z
);
80 void check(JSObject
* obj
) {
82 check(obj
->compartment());
86 void check(const Rooted
<T
>& rooted
) {
91 void check(Handle
<T
> handle
) {
95 void check(JSString
* str
) {
97 checkZone(str
->zone());
100 void check(const js::Value
& v
) {
102 check(&v
.toObject());
103 else if (v
.isString())
107 void check(const ValueArray
& arr
) {
108 for (size_t i
= 0; i
< arr
.length
; i
++)
112 void check(const JSValueArray
& arr
) {
113 for (size_t i
= 0; i
< arr
.length
; i
++)
117 void check(const JS::HandleValueArray
& arr
) {
118 for (size_t i
= 0; i
< arr
.length(); i
++)
122 void check(const CallArgs
& args
) {
123 for (Value
* p
= args
.base(); p
!= args
.end(); ++p
)
127 void check(jsid id
) {}
129 void check(JSScript
* script
) {
131 check(script
->compartment());
134 void check(InterpreterFrame
* fp
);
135 void check(AbstractFramePtr frame
);
136 void check(SavedStacks
* stacks
);
138 #endif /* JS_CRASH_DIAGNOSTICS */
141 * Don't perform these checks when called from a finalizer. The checking
142 * depends on other objects not having been swept yet.
144 #define START_ASSERT_SAME_COMPARTMENT() \
145 if (cx->isJSContext() && cx->asJSContext()->runtime()->isHeapBusy()) \
147 CompartmentChecker c(cx)
149 template <class T1
> inline void
150 assertSameCompartment(ExclusiveContext
* cx
, const T1
& t1
)
152 #ifdef JS_CRASH_DIAGNOSTICS
153 START_ASSERT_SAME_COMPARTMENT();
158 template <class T1
> inline void
159 assertSameCompartmentDebugOnly(ExclusiveContext
* cx
, const T1
& t1
)
161 #if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
162 START_ASSERT_SAME_COMPARTMENT();
167 template <class T1
, class T2
> inline void
168 assertSameCompartment(ExclusiveContext
* cx
, const T1
& t1
, const T2
& t2
)
170 #ifdef JS_CRASH_DIAGNOSTICS
171 START_ASSERT_SAME_COMPARTMENT();
177 template <class T1
, class T2
, class T3
> inline void
178 assertSameCompartment(ExclusiveContext
* cx
, const T1
& t1
, const T2
& t2
, const T3
& t3
)
180 #ifdef JS_CRASH_DIAGNOSTICS
181 START_ASSERT_SAME_COMPARTMENT();
188 template <class T1
, class T2
, class T3
, class T4
> inline void
189 assertSameCompartment(ExclusiveContext
* cx
,
190 const T1
& t1
, const T2
& t2
, const T3
& t3
, const T4
& t4
)
192 #ifdef JS_CRASH_DIAGNOSTICS
193 START_ASSERT_SAME_COMPARTMENT();
201 template <class T1
, class T2
, class T3
, class T4
, class T5
> inline void
202 assertSameCompartment(ExclusiveContext
* cx
,
203 const T1
& t1
, const T2
& t2
, const T3
& t3
, const T4
& t4
, const T5
& t5
)
205 #ifdef JS_CRASH_DIAGNOSTICS
206 START_ASSERT_SAME_COMPARTMENT();
215 #undef START_ASSERT_SAME_COMPARTMENT
217 STATIC_PRECONDITION_ASSUME(ubound(args
.argv_
) >= argc
)
218 MOZ_ALWAYS_INLINE
bool
219 CallJSNative(JSContext
* cx
, Native native
, const CallArgs
& args
)
221 JS_CHECK_RECURSION(cx
, return false);
224 bool alreadyThrowing
= cx
->isExceptionPending();
226 assertSameCompartment(cx
, args
);
227 bool ok
= native(cx
, args
.length(), args
.base());
229 assertSameCompartment(cx
, args
.rval());
230 MOZ_ASSERT_IF(!alreadyThrowing
, !cx
->isExceptionPending());
235 STATIC_PRECONDITION_ASSUME(ubound(args
.argv_
) >= argc
)
236 MOZ_ALWAYS_INLINE
bool
237 CallNativeImpl(JSContext
* cx
, NativeImpl impl
, const CallArgs
& args
)
240 bool alreadyThrowing
= cx
->isExceptionPending();
242 assertSameCompartment(cx
, args
);
243 bool ok
= impl(cx
, args
);
245 assertSameCompartment(cx
, args
.rval());
246 MOZ_ASSERT_IF(!alreadyThrowing
, !cx
->isExceptionPending());
251 STATIC_PRECONDITION(ubound(args
.argv_
) >= argc
)
252 MOZ_ALWAYS_INLINE
bool
253 CallJSNativeConstructor(JSContext
* cx
, Native native
, const CallArgs
& args
)
256 RootedObject
callee(cx
, &args
.callee());
259 MOZ_ASSERT(args
.thisv().isMagic());
260 if (!CallJSNative(cx
, native
, args
))
264 * Native constructors must return non-primitive values on success.
265 * Although it is legal, if a constructor returns the callee, there is a
266 * 99.9999% chance it is a bug. If any valid code actually wants the
267 * constructor to return the callee, the assertion can be removed or
268 * (another) conjunct can be added to the antecedent.
272 * - Proxies are exceptions to both rules: they can return primitives and
273 * they allow content to return the callee.
275 * - CallOrConstructBoundFunction is an exception as well because we might
276 * have used bind on a proxy function.
278 * - new Iterator(x) is user-hookable; it returns x.__iterator__() which
279 * could be any object.
281 * - (new Object(Object)) returns the callee.
283 MOZ_ASSERT_IF(native
!= js::proxy_Construct
&&
284 native
!= js::CallOrConstructBoundFunction
&&
285 native
!= js::IteratorConstructor
&&
286 (!callee
->is
<JSFunction
>() || callee
->as
<JSFunction
>().native() != obj_construct
),
287 args
.rval().isObject() && callee
!= &args
.rval().toObject());
292 MOZ_ALWAYS_INLINE
bool
293 CallJSPropertyOp(JSContext
* cx
, PropertyOp op
, HandleObject receiver
, HandleId id
, MutableHandleValue vp
)
295 JS_CHECK_RECURSION(cx
, return false);
297 assertSameCompartment(cx
, receiver
, id
, vp
);
298 bool ok
= op(cx
, receiver
, id
, vp
);
300 assertSameCompartment(cx
, vp
);
304 MOZ_ALWAYS_INLINE
bool
305 CallJSPropertyOpSetter(JSContext
* cx
, StrictPropertyOp op
, HandleObject obj
, HandleId id
,
306 bool strict
, MutableHandleValue vp
)
308 JS_CHECK_RECURSION(cx
, return false);
310 assertSameCompartment(cx
, obj
, id
, vp
);
311 return op(cx
, obj
, id
, strict
, vp
);
315 CallJSDeletePropertyOp(JSContext
* cx
, JSDeletePropertyOp op
, HandleObject receiver
, HandleId id
,
318 JS_CHECK_RECURSION(cx
, return false);
320 assertSameCompartment(cx
, receiver
, id
);
322 return op(cx
, receiver
, id
, succeeded
);
328 CallSetter(JSContext
* cx
, HandleObject obj
, HandleId id
, StrictPropertyOp op
, unsigned attrs
,
329 bool strict
, MutableHandleValue vp
)
331 if (attrs
& JSPROP_SETTER
) {
332 RootedValue
opv(cx
, CastAsObjectJsval(op
));
333 return InvokeGetterOrSetter(cx
, obj
, opv
, 1, vp
.address(), vp
);
336 if (attrs
& JSPROP_GETTER
)
337 return js_ReportGetterOnlyAssignment(cx
, strict
);
342 return CallJSPropertyOpSetter(cx
, op
, obj
, id
, strict
, vp
);
346 GetNativeStackLimit(ExclusiveContext
* cx
)
349 if (cx
->isJSContext()) {
350 kind
= cx
->asJSContext()->runningWithTrustedPrincipals()
351 ? StackForTrustedScript
: StackForUntrustedScript
;
353 // For other threads, we just use the trusted stack depth, since it's
354 // unlikely that we'll be mixing trusted and untrusted code together.
355 kind
= StackForTrustedScript
;
357 return cx
->perThreadData
->nativeStackLimit
[kind
];
361 ExclusiveContext::typeLifoAlloc()
363 return zone()->types
.typeLifoAlloc
;
369 JSContext::setPendingException(js::Value v
)
371 MOZ_ASSERT(!IsPoisonedValue(v
));
372 // overRecursed_ is set after the fact by js_ReportOverRecursed.
373 this->overRecursed_
= false;
374 this->throwing
= true;
375 this->unwrappedException_
= v
;
376 // We don't use assertSameCompartment here to allow
377 // js::SetPendingExceptionCrossContext to work.
378 MOZ_ASSERT_IF(v
.isObject(), v
.toObject().compartment() == compartment());
382 JSContext::runningWithTrustedPrincipals() const
384 return !compartment() || compartment()->principals
== runtime()->trustedPrincipals();
388 js::ExclusiveContext::enterCompartment(JSCompartment
* c
)
390 enterCompartmentDepth_
++;
396 js::ExclusiveContext::enterNullCompartment()
398 enterCompartmentDepth_
++;
399 setCompartment(nullptr);
403 js::ExclusiveContext::leaveCompartment(JSCompartment
* oldCompartment
)
405 MOZ_ASSERT(hasEnteredCompartment());
406 enterCompartmentDepth_
--;
408 // Only call leave() after we've setCompartment()-ed away from the current
410 JSCompartment
* startingCompartment
= compartment_
;
411 setCompartment(oldCompartment
);
412 if (startingCompartment
)
413 startingCompartment
->leave();
417 js::ExclusiveContext::setCompartment(JSCompartment
* comp
)
419 // ExclusiveContexts can only be in the atoms zone or in exclusive zones.
420 MOZ_ASSERT_IF(!isJSContext() && !runtime_
->isAtomsCompartment(comp
),
421 comp
->zone()->usedByExclusiveThread
);
423 // Normal JSContexts cannot enter exclusive zones.
424 MOZ_ASSERT_IF(isJSContext() && comp
,
425 !comp
->zone()->usedByExclusiveThread
);
427 // Only one thread can be in the atoms compartment at a time.
428 MOZ_ASSERT_IF(runtime_
->isAtomsCompartment(comp
),
429 runtime_
->currentThreadHasExclusiveAccess());
431 // Make sure that the atoms compartment has its own zone.
432 MOZ_ASSERT_IF(comp
&& !runtime_
->isAtomsCompartment(comp
),
433 !runtime_
->isAtomsZone(comp
->zone()));
435 // Both the current and the new compartment should be properly marked as
436 // entered at this point.
437 MOZ_ASSERT_IF(compartment_
, compartment_
->hasBeenEntered());
438 MOZ_ASSERT_IF(comp
, comp
->hasBeenEntered());
441 zone_
= comp
? comp
->zone() : nullptr;
442 allocator_
= zone_
? &zone_
->allocator
: nullptr;
446 JSContext::currentScript(jsbytecode
** ppc
,
447 MaybeAllowCrossCompartment allowCrossCompartment
) const
452 js::Activation
* act
= mainThread().activation();
453 while (act
&& (act
->cx() != this || (act
->isJit() && !act
->asJit()->isActive())))
459 MOZ_ASSERT(act
->cx() == this);
462 JSScript
* script
= nullptr;
463 js::jit::GetPcScript(const_cast<JSContext
*>(this), &script
, ppc
);
464 if (!allowCrossCompartment
&& script
->compartment() != compartment())
472 MOZ_ASSERT(act
->isInterpreter());
474 js::InterpreterFrame
* fp
= act
->asInterpreter()->current();
475 MOZ_ASSERT(!fp
->runningInJit());
477 JSScript
* script
= fp
->script();
478 if (!allowCrossCompartment
&& script
->compartment() != compartment())
482 *ppc
= act
->asInterpreter()->regs().pc
;
483 MOZ_ASSERT(script
->containsPC(*ppc
));
488 template <JSThreadSafeNative threadSafeNative
>
490 JSNativeThreadSafeWrapper(JSContext
* cx
, unsigned argc
, JS::Value
* vp
)
492 return threadSafeNative(cx
, argc
, vp
);
495 template <JSThreadSafeNative threadSafeNative
>
497 JSParallelNativeThreadSafeWrapper(js::ForkJoinContext
* cx
, unsigned argc
, JS::Value
* vp
)
499 return threadSafeNative(cx
, argc
, vp
);
502 /* static */ inline JSContext
*
503 js::ExecutionModeTraits
<js::SequentialExecution
>::toContextType(ExclusiveContext
* cx
)
505 return cx
->asJSContext();
508 #endif /* jscntxtinlines_h */