1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jscntxtinlines.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,525 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef jscntxtinlines_h 1.11 +#define jscntxtinlines_h 1.12 + 1.13 +#include "jscntxt.h" 1.14 +#include "jscompartment.h" 1.15 + 1.16 +#include "jsiter.h" 1.17 +#include "jsworkers.h" 1.18 + 1.19 +#include "builtin/Object.h" 1.20 +#include "jit/IonFrames.h" 1.21 +#include "vm/ForkJoin.h" 1.22 +#include "vm/Interpreter.h" 1.23 +#include "vm/ProxyObject.h" 1.24 + 1.25 +namespace js { 1.26 + 1.27 +#ifdef JS_CRASH_DIAGNOSTICS 1.28 +class CompartmentChecker 1.29 +{ 1.30 + JSCompartment *compartment; 1.31 + 1.32 + public: 1.33 + explicit CompartmentChecker(ExclusiveContext *cx) 1.34 + : compartment(cx->compartment()) 1.35 + { 1.36 +#ifdef DEBUG 1.37 + // In debug builds, make sure the embedder passed the cx it claimed it 1.38 + // was going to use. 1.39 + JSContext *activeContext = nullptr; 1.40 + if (cx->isJSContext()) 1.41 + activeContext = cx->asJSContext()->runtime()->activeContext; 1.42 + JS_ASSERT_IF(activeContext, cx == activeContext); 1.43 +#endif 1.44 + } 1.45 + 1.46 + /* 1.47 + * Set a breakpoint here (break js::CompartmentChecker::fail) to debug 1.48 + * compartment mismatches. 1.49 + */ 1.50 + static void fail(JSCompartment *c1, JSCompartment *c2) { 1.51 + printf("*** Compartment mismatch %p vs. %p\n", (void *) c1, (void *) c2); 1.52 + MOZ_CRASH(); 1.53 + } 1.54 + 1.55 + static void fail(JS::Zone *z1, JS::Zone *z2) { 1.56 + printf("*** Zone mismatch %p vs. %p\n", (void *) z1, (void *) z2); 1.57 + MOZ_CRASH(); 1.58 + } 1.59 + 1.60 + /* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */ 1.61 + static void check(JSCompartment *c1, JSCompartment *c2) { 1.62 + JS_ASSERT(!c1->runtimeFromAnyThread()->isAtomsCompartment(c1)); 1.63 + JS_ASSERT(!c2->runtimeFromAnyThread()->isAtomsCompartment(c2)); 1.64 + if (c1 != c2) 1.65 + fail(c1, c2); 1.66 + } 1.67 + 1.68 + void check(JSCompartment *c) { 1.69 + if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) { 1.70 + if (!compartment) 1.71 + compartment = c; 1.72 + else if (c != compartment) 1.73 + fail(compartment, c); 1.74 + } 1.75 + } 1.76 + 1.77 + void checkZone(JS::Zone *z) { 1.78 + if (compartment && z != compartment->zone()) 1.79 + fail(compartment->zone(), z); 1.80 + } 1.81 + 1.82 + void check(JSObject *obj) { 1.83 + if (obj) 1.84 + check(obj->compartment()); 1.85 + } 1.86 + 1.87 + template<typename T> 1.88 + void check(const Rooted<T>& rooted) { 1.89 + check(rooted.get()); 1.90 + } 1.91 + 1.92 + template<typename T> 1.93 + void check(Handle<T> handle) { 1.94 + check(handle.get()); 1.95 + } 1.96 + 1.97 + void check(JSString *str) { 1.98 + if (!str->isAtom()) 1.99 + checkZone(str->zone()); 1.100 + } 1.101 + 1.102 + void check(const js::Value &v) { 1.103 + if (v.isObject()) 1.104 + check(&v.toObject()); 1.105 + else if (v.isString()) 1.106 + check(v.toString()); 1.107 + } 1.108 + 1.109 + void check(const ValueArray &arr) { 1.110 + for (size_t i = 0; i < arr.length; i++) 1.111 + check(arr.array[i]); 1.112 + } 1.113 + 1.114 + void check(const JSValueArray &arr) { 1.115 + for (size_t i = 0; i < arr.length; i++) 1.116 + check(arr.array[i]); 1.117 + } 1.118 + 1.119 + void check(const JS::HandleValueArray &arr) { 1.120 + for (size_t i = 0; i < arr.length(); i++) 1.121 + check(arr[i]); 1.122 + } 1.123 + 1.124 + void check(const CallArgs &args) { 1.125 + for (Value *p = args.base(); p != args.end(); ++p) 1.126 + check(*p); 1.127 + } 1.128 + 1.129 + void check(jsid id) { 1.130 + if (JSID_IS_OBJECT(id)) 1.131 + check(JSID_TO_OBJECT(id)); 1.132 + } 1.133 + 1.134 + void check(JSIdArray *ida) { 1.135 + if (ida) { 1.136 + for (int i = 0; i < ida->length; i++) { 1.137 + if (JSID_IS_OBJECT(ida->vector[i])) 1.138 + check(ida->vector[i]); 1.139 + } 1.140 + } 1.141 + } 1.142 + 1.143 + void check(JSScript *script) { 1.144 + if (script) 1.145 + check(script->compartment()); 1.146 + } 1.147 + 1.148 + void check(InterpreterFrame *fp); 1.149 + void check(AbstractFramePtr frame); 1.150 +}; 1.151 +#endif /* JS_CRASH_DIAGNOSTICS */ 1.152 + 1.153 +/* 1.154 + * Don't perform these checks when called from a finalizer. The checking 1.155 + * depends on other objects not having been swept yet. 1.156 + */ 1.157 +#define START_ASSERT_SAME_COMPARTMENT() \ 1.158 + if (!cx->isExclusiveContext()) \ 1.159 + return; \ 1.160 + if (cx->isJSContext() && cx->asJSContext()->runtime()->isHeapBusy()) \ 1.161 + return; \ 1.162 + CompartmentChecker c(cx->asExclusiveContext()) 1.163 + 1.164 +template <class T1> inline void 1.165 +assertSameCompartment(ThreadSafeContext *cx, const T1 &t1) 1.166 +{ 1.167 +#ifdef JS_CRASH_DIAGNOSTICS 1.168 + START_ASSERT_SAME_COMPARTMENT(); 1.169 + c.check(t1); 1.170 +#endif 1.171 +} 1.172 + 1.173 +template <class T1> inline void 1.174 +assertSameCompartmentDebugOnly(ThreadSafeContext *cx, const T1 &t1) 1.175 +{ 1.176 +#ifdef DEBUG 1.177 + START_ASSERT_SAME_COMPARTMENT(); 1.178 + c.check(t1); 1.179 +#endif 1.180 +} 1.181 + 1.182 +template <class T1, class T2> inline void 1.183 +assertSameCompartment(ThreadSafeContext *cx, const T1 &t1, const T2 &t2) 1.184 +{ 1.185 +#ifdef JS_CRASH_DIAGNOSTICS 1.186 + START_ASSERT_SAME_COMPARTMENT(); 1.187 + c.check(t1); 1.188 + c.check(t2); 1.189 +#endif 1.190 +} 1.191 + 1.192 +template <class T1, class T2, class T3> inline void 1.193 +assertSameCompartment(ThreadSafeContext *cx, const T1 &t1, const T2 &t2, const T3 &t3) 1.194 +{ 1.195 +#ifdef JS_CRASH_DIAGNOSTICS 1.196 + START_ASSERT_SAME_COMPARTMENT(); 1.197 + c.check(t1); 1.198 + c.check(t2); 1.199 + c.check(t3); 1.200 +#endif 1.201 +} 1.202 + 1.203 +template <class T1, class T2, class T3, class T4> inline void 1.204 +assertSameCompartment(ThreadSafeContext *cx, 1.205 + const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) 1.206 +{ 1.207 +#ifdef JS_CRASH_DIAGNOSTICS 1.208 + START_ASSERT_SAME_COMPARTMENT(); 1.209 + c.check(t1); 1.210 + c.check(t2); 1.211 + c.check(t3); 1.212 + c.check(t4); 1.213 +#endif 1.214 +} 1.215 + 1.216 +template <class T1, class T2, class T3, class T4, class T5> inline void 1.217 +assertSameCompartment(ThreadSafeContext *cx, 1.218 + const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5) 1.219 +{ 1.220 +#ifdef JS_CRASH_DIAGNOSTICS 1.221 + START_ASSERT_SAME_COMPARTMENT(); 1.222 + c.check(t1); 1.223 + c.check(t2); 1.224 + c.check(t3); 1.225 + c.check(t4); 1.226 + c.check(t5); 1.227 +#endif 1.228 +} 1.229 + 1.230 +#undef START_ASSERT_SAME_COMPARTMENT 1.231 + 1.232 +STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc) 1.233 +MOZ_ALWAYS_INLINE bool 1.234 +CallJSNative(JSContext *cx, Native native, const CallArgs &args) 1.235 +{ 1.236 + JS_CHECK_RECURSION(cx, return false); 1.237 + 1.238 +#ifdef DEBUG 1.239 + bool alreadyThrowing = cx->isExceptionPending(); 1.240 +#endif 1.241 + assertSameCompartment(cx, args); 1.242 + bool ok = native(cx, args.length(), args.base()); 1.243 + if (ok) { 1.244 + assertSameCompartment(cx, args.rval()); 1.245 + JS_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending()); 1.246 + } 1.247 + return ok; 1.248 +} 1.249 + 1.250 +STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc) 1.251 +MOZ_ALWAYS_INLINE bool 1.252 +CallNativeImpl(JSContext *cx, NativeImpl impl, const CallArgs &args) 1.253 +{ 1.254 +#ifdef DEBUG 1.255 + bool alreadyThrowing = cx->isExceptionPending(); 1.256 +#endif 1.257 + assertSameCompartment(cx, args); 1.258 + bool ok = impl(cx, args); 1.259 + if (ok) { 1.260 + assertSameCompartment(cx, args.rval()); 1.261 + JS_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending()); 1.262 + } 1.263 + return ok; 1.264 +} 1.265 + 1.266 +STATIC_PRECONDITION(ubound(args.argv_) >= argc) 1.267 +MOZ_ALWAYS_INLINE bool 1.268 +CallJSNativeConstructor(JSContext *cx, Native native, const CallArgs &args) 1.269 +{ 1.270 +#ifdef DEBUG 1.271 + RootedObject callee(cx, &args.callee()); 1.272 +#endif 1.273 + 1.274 + JS_ASSERT(args.thisv().isMagic()); 1.275 + if (!CallJSNative(cx, native, args)) 1.276 + return false; 1.277 + 1.278 + /* 1.279 + * Native constructors must return non-primitive values on success. 1.280 + * Although it is legal, if a constructor returns the callee, there is a 1.281 + * 99.9999% chance it is a bug. If any valid code actually wants the 1.282 + * constructor to return the callee, the assertion can be removed or 1.283 + * (another) conjunct can be added to the antecedent. 1.284 + * 1.285 + * Exceptions: 1.286 + * 1.287 + * - Proxies are exceptions to both rules: they can return primitives and 1.288 + * they allow content to return the callee. 1.289 + * 1.290 + * - CallOrConstructBoundFunction is an exception as well because we might 1.291 + * have used bind on a proxy function. 1.292 + * 1.293 + * - new Iterator(x) is user-hookable; it returns x.__iterator__() which 1.294 + * could be any object. 1.295 + * 1.296 + * - (new Object(Object)) returns the callee. 1.297 + */ 1.298 + JS_ASSERT_IF(native != ProxyObject::callableClass_.construct && 1.299 + native != js::CallOrConstructBoundFunction && 1.300 + native != js::IteratorConstructor && 1.301 + (!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct), 1.302 + !args.rval().isPrimitive() && callee != &args.rval().toObject()); 1.303 + 1.304 + return true; 1.305 +} 1.306 + 1.307 +MOZ_ALWAYS_INLINE bool 1.308 +CallJSPropertyOp(JSContext *cx, PropertyOp op, HandleObject receiver, HandleId id, MutableHandleValue vp) 1.309 +{ 1.310 + JS_CHECK_RECURSION(cx, return false); 1.311 + 1.312 + assertSameCompartment(cx, receiver, id, vp); 1.313 + bool ok = op(cx, receiver, id, vp); 1.314 + if (ok) 1.315 + assertSameCompartment(cx, vp); 1.316 + return ok; 1.317 +} 1.318 + 1.319 +MOZ_ALWAYS_INLINE bool 1.320 +CallJSPropertyOpSetter(JSContext *cx, StrictPropertyOp op, HandleObject obj, HandleId id, 1.321 + bool strict, MutableHandleValue vp) 1.322 +{ 1.323 + JS_CHECK_RECURSION(cx, return false); 1.324 + 1.325 + assertSameCompartment(cx, obj, id, vp); 1.326 + return op(cx, obj, id, strict, vp); 1.327 +} 1.328 + 1.329 +static inline bool 1.330 +CallJSDeletePropertyOp(JSContext *cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id, 1.331 + bool *succeeded) 1.332 +{ 1.333 + JS_CHECK_RECURSION(cx, return false); 1.334 + 1.335 + assertSameCompartment(cx, receiver, id); 1.336 + return op(cx, receiver, id, succeeded); 1.337 +} 1.338 + 1.339 +inline bool 1.340 +CallSetter(JSContext *cx, HandleObject obj, HandleId id, StrictPropertyOp op, unsigned attrs, 1.341 + bool strict, MutableHandleValue vp) 1.342 +{ 1.343 + if (attrs & JSPROP_SETTER) { 1.344 + RootedValue opv(cx, CastAsObjectJsval(op)); 1.345 + return InvokeGetterOrSetter(cx, obj, opv, 1, vp.address(), vp); 1.346 + } 1.347 + 1.348 + if (attrs & JSPROP_GETTER) 1.349 + return js_ReportGetterOnlyAssignment(cx, strict); 1.350 + 1.351 + return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp); 1.352 +} 1.353 + 1.354 +inline uintptr_t 1.355 +GetNativeStackLimit(ThreadSafeContext *cx) 1.356 +{ 1.357 + StackKind kind; 1.358 + if (cx->isJSContext()) { 1.359 + kind = cx->asJSContext()->runningWithTrustedPrincipals() 1.360 + ? StackForTrustedScript : StackForUntrustedScript; 1.361 + } else { 1.362 + // For other threads, we just use the trusted stack depth, since it's 1.363 + // unlikely that we'll be mixing trusted and untrusted code together. 1.364 + kind = StackForTrustedScript; 1.365 + } 1.366 + return cx->perThreadData->nativeStackLimit[kind]; 1.367 +} 1.368 + 1.369 +inline LifoAlloc & 1.370 +ExclusiveContext::typeLifoAlloc() 1.371 +{ 1.372 + return zone()->types.typeLifoAlloc; 1.373 +} 1.374 + 1.375 +} /* namespace js */ 1.376 + 1.377 +inline void 1.378 +JSContext::setPendingException(js::Value v) 1.379 +{ 1.380 + JS_ASSERT(!IsPoisonedValue(v)); 1.381 + this->throwing = true; 1.382 + this->unwrappedException_ = v; 1.383 + // We don't use assertSameCompartment here to allow 1.384 + // js::SetPendingExceptionCrossContext to work. 1.385 + JS_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment()); 1.386 +} 1.387 + 1.388 +inline void 1.389 +JSContext::setDefaultCompartmentObject(JSObject *obj) 1.390 +{ 1.391 + JS_ASSERT(!options().noDefaultCompartmentObject()); 1.392 + defaultCompartmentObject_ = obj; 1.393 +} 1.394 + 1.395 +inline void 1.396 +JSContext::setDefaultCompartmentObjectIfUnset(JSObject *obj) 1.397 +{ 1.398 + if (!options().noDefaultCompartmentObject() && 1.399 + !defaultCompartmentObject_) 1.400 + { 1.401 + setDefaultCompartmentObject(obj); 1.402 + } 1.403 +} 1.404 + 1.405 +inline void 1.406 +js::ExclusiveContext::enterCompartment(JSCompartment *c) 1.407 +{ 1.408 + enterCompartmentDepth_++; 1.409 + c->enter(); 1.410 + setCompartment(c); 1.411 +} 1.412 + 1.413 +inline void 1.414 +js::ExclusiveContext::enterNullCompartment() 1.415 +{ 1.416 + enterCompartmentDepth_++; 1.417 + setCompartment(nullptr); 1.418 +} 1.419 + 1.420 +inline void 1.421 +js::ExclusiveContext::leaveCompartment(JSCompartment *oldCompartment) 1.422 +{ 1.423 + JS_ASSERT(hasEnteredCompartment()); 1.424 + enterCompartmentDepth_--; 1.425 + 1.426 + // Only call leave() after we've setCompartment()-ed away from the current 1.427 + // compartment. 1.428 + JSCompartment *startingCompartment = compartment_; 1.429 + setCompartment(oldCompartment); 1.430 + if (startingCompartment) 1.431 + startingCompartment->leave(); 1.432 +} 1.433 + 1.434 +inline void 1.435 +js::ExclusiveContext::setCompartment(JSCompartment *comp) 1.436 +{ 1.437 + // ExclusiveContexts can only be in the atoms zone or in exclusive zones. 1.438 + JS_ASSERT_IF(!isJSContext() && !runtime_->isAtomsCompartment(comp), 1.439 + comp->zone()->usedByExclusiveThread); 1.440 + 1.441 + // Normal JSContexts cannot enter exclusive zones. 1.442 + JS_ASSERT_IF(isJSContext() && comp, 1.443 + !comp->zone()->usedByExclusiveThread); 1.444 + 1.445 + // Only one thread can be in the atoms compartment at a time. 1.446 + JS_ASSERT_IF(runtime_->isAtomsCompartment(comp), 1.447 + runtime_->currentThreadHasExclusiveAccess()); 1.448 + 1.449 + // Make sure that the atoms compartment has its own zone. 1.450 + JS_ASSERT_IF(comp && !runtime_->isAtomsCompartment(comp), 1.451 + !runtime_->isAtomsZone(comp->zone())); 1.452 + 1.453 + // Both the current and the new compartment should be properly marked as 1.454 + // entered at this point. 1.455 + JS_ASSERT_IF(compartment_, compartment_->hasBeenEntered()); 1.456 + JS_ASSERT_IF(comp, comp->hasBeenEntered()); 1.457 + 1.458 + compartment_ = comp; 1.459 + zone_ = comp ? comp->zone() : nullptr; 1.460 + allocator_ = zone_ ? &zone_->allocator : nullptr; 1.461 +} 1.462 + 1.463 +inline JSScript * 1.464 +JSContext::currentScript(jsbytecode **ppc, 1.465 + MaybeAllowCrossCompartment allowCrossCompartment) const 1.466 +{ 1.467 + if (ppc) 1.468 + *ppc = nullptr; 1.469 + 1.470 + js::Activation *act = mainThread().activation(); 1.471 + while (act && (act->cx() != this || (act->isJit() && !act->asJit()->isActive()))) 1.472 + act = act->prev(); 1.473 + 1.474 + if (!act) 1.475 + return nullptr; 1.476 + 1.477 + JS_ASSERT(act->cx() == this); 1.478 + 1.479 +#ifdef JS_ION 1.480 + if (act->isJit()) { 1.481 + JSScript *script = nullptr; 1.482 + js::jit::GetPcScript(const_cast<JSContext *>(this), &script, ppc); 1.483 + if (!allowCrossCompartment && script->compartment() != compartment()) 1.484 + return nullptr; 1.485 + return script; 1.486 + } 1.487 + 1.488 + if (act->isAsmJS()) 1.489 + return nullptr; 1.490 +#endif 1.491 + 1.492 + JS_ASSERT(act->isInterpreter()); 1.493 + 1.494 + js::InterpreterFrame *fp = act->asInterpreter()->current(); 1.495 + JS_ASSERT(!fp->runningInJit()); 1.496 + 1.497 + JSScript *script = fp->script(); 1.498 + if (!allowCrossCompartment && script->compartment() != compartment()) 1.499 + return nullptr; 1.500 + 1.501 + if (ppc) { 1.502 + *ppc = act->asInterpreter()->regs().pc; 1.503 + JS_ASSERT(script->containsPC(*ppc)); 1.504 + } 1.505 + return script; 1.506 +} 1.507 + 1.508 +template <JSThreadSafeNative threadSafeNative> 1.509 +inline bool 1.510 +JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp) 1.511 +{ 1.512 + return threadSafeNative(cx, argc, vp); 1.513 +} 1.514 + 1.515 +template <JSThreadSafeNative threadSafeNative> 1.516 +inline bool 1.517 +JSParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp) 1.518 +{ 1.519 + return threadSafeNative(cx, argc, vp); 1.520 +} 1.521 + 1.522 +/* static */ inline JSContext * 1.523 +js::ExecutionModeTraits<js::SequentialExecution>::toContextType(ExclusiveContext *cx) 1.524 +{ 1.525 + return cx->asJSContext(); 1.526 +} 1.527 + 1.528 +#endif /* jscntxtinlines_h */