|
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/. */ |
|
6 |
|
7 #ifndef jscompartment_h |
|
8 #define jscompartment_h |
|
9 |
|
10 #include "mozilla/MemoryReporting.h" |
|
11 |
|
12 #include "builtin/TypedObject.h" |
|
13 #include "gc/Zone.h" |
|
14 #include "vm/GlobalObject.h" |
|
15 #include "vm/PIC.h" |
|
16 #include "vm/SavedStacks.h" |
|
17 |
|
18 namespace js { |
|
19 |
|
20 namespace jit { |
|
21 class JitCompartment; |
|
22 } |
|
23 |
|
24 namespace gc { |
|
25 template<class Node> class ComponentFinder; |
|
26 } |
|
27 |
|
28 struct NativeIterator; |
|
29 |
|
30 /* |
|
31 * A single-entry cache for some base-10 double-to-string conversions. This |
|
32 * helps date-format-xparb.js. It also avoids skewing the results for |
|
33 * v8-splay.js when measured by the SunSpider harness, where the splay tree |
|
34 * initialization (which includes many repeated double-to-string conversions) |
|
35 * is erroneously included in the measurement; see bug 562553. |
|
36 */ |
|
37 class DtoaCache { |
|
38 double d; |
|
39 int base; |
|
40 JSFlatString *s; // if s==nullptr, d and base are not valid |
|
41 |
|
42 public: |
|
43 DtoaCache() : s(nullptr) {} |
|
44 void purge() { s = nullptr; } |
|
45 |
|
46 JSFlatString *lookup(int base, double d) { |
|
47 return this->s && base == this->base && d == this->d ? this->s : nullptr; |
|
48 } |
|
49 |
|
50 void cache(int base, double d, JSFlatString *s) { |
|
51 this->base = base; |
|
52 this->d = d; |
|
53 this->s = s; |
|
54 } |
|
55 }; |
|
56 |
|
57 /* If HashNumber grows, need to change WrapperHasher. */ |
|
58 JS_STATIC_ASSERT(sizeof(HashNumber) == 4); |
|
59 |
|
60 struct CrossCompartmentKey |
|
61 { |
|
62 enum Kind { |
|
63 ObjectWrapper, |
|
64 StringWrapper, |
|
65 DebuggerScript, |
|
66 DebuggerSource, |
|
67 DebuggerObject, |
|
68 DebuggerEnvironment |
|
69 }; |
|
70 |
|
71 Kind kind; |
|
72 JSObject *debugger; |
|
73 js::gc::Cell *wrapped; |
|
74 |
|
75 CrossCompartmentKey() |
|
76 : kind(ObjectWrapper), debugger(nullptr), wrapped(nullptr) {} |
|
77 CrossCompartmentKey(JSObject *wrapped) |
|
78 : kind(ObjectWrapper), debugger(nullptr), wrapped(wrapped) {} |
|
79 CrossCompartmentKey(JSString *wrapped) |
|
80 : kind(StringWrapper), debugger(nullptr), wrapped(wrapped) {} |
|
81 CrossCompartmentKey(Value wrapped) |
|
82 : kind(wrapped.isString() ? StringWrapper : ObjectWrapper), |
|
83 debugger(nullptr), |
|
84 wrapped((js::gc::Cell *)wrapped.toGCThing()) {} |
|
85 CrossCompartmentKey(const RootedValue &wrapped) |
|
86 : kind(wrapped.get().isString() ? StringWrapper : ObjectWrapper), |
|
87 debugger(nullptr), |
|
88 wrapped((js::gc::Cell *)wrapped.get().toGCThing()) {} |
|
89 CrossCompartmentKey(Kind kind, JSObject *dbg, js::gc::Cell *wrapped) |
|
90 : kind(kind), debugger(dbg), wrapped(wrapped) {} |
|
91 }; |
|
92 |
|
93 struct WrapperHasher : public DefaultHasher<CrossCompartmentKey> |
|
94 { |
|
95 static HashNumber hash(const CrossCompartmentKey &key) { |
|
96 JS_ASSERT(!IsPoisonedPtr(key.wrapped)); |
|
97 return uint32_t(uintptr_t(key.wrapped)) | uint32_t(key.kind); |
|
98 } |
|
99 |
|
100 static bool match(const CrossCompartmentKey &l, const CrossCompartmentKey &k) { |
|
101 return l.kind == k.kind && l.debugger == k.debugger && l.wrapped == k.wrapped; |
|
102 } |
|
103 }; |
|
104 |
|
105 typedef HashMap<CrossCompartmentKey, ReadBarrieredValue, |
|
106 WrapperHasher, SystemAllocPolicy> WrapperMap; |
|
107 |
|
108 } /* namespace js */ |
|
109 |
|
110 namespace JS { |
|
111 struct TypeInferenceSizes; |
|
112 } |
|
113 |
|
114 namespace js { |
|
115 class AutoDebugModeInvalidation; |
|
116 class DebugScopes; |
|
117 class WeakMapBase; |
|
118 } |
|
119 |
|
120 struct JSCompartment |
|
121 { |
|
122 JS::CompartmentOptions options_; |
|
123 |
|
124 private: |
|
125 JS::Zone *zone_; |
|
126 JSRuntime *runtime_; |
|
127 |
|
128 public: |
|
129 JSPrincipals *principals; |
|
130 bool isSystem; |
|
131 bool isSelfHosting; |
|
132 bool marked; |
|
133 |
|
134 #ifdef DEBUG |
|
135 bool firedOnNewGlobalObject; |
|
136 #endif |
|
137 |
|
138 void mark() { marked = true; } |
|
139 |
|
140 private: |
|
141 friend struct JSRuntime; |
|
142 friend struct JSContext; |
|
143 friend class js::ExclusiveContext; |
|
144 js::ReadBarriered<js::GlobalObject> global_; |
|
145 |
|
146 unsigned enterCompartmentDepth; |
|
147 |
|
148 public: |
|
149 void enter() { enterCompartmentDepth++; } |
|
150 void leave() { enterCompartmentDepth--; } |
|
151 bool hasBeenEntered() { return !!enterCompartmentDepth; } |
|
152 |
|
153 JS::Zone *zone() { return zone_; } |
|
154 const JS::Zone *zone() const { return zone_; } |
|
155 JS::CompartmentOptions &options() { return options_; } |
|
156 const JS::CompartmentOptions &options() const { return options_; } |
|
157 |
|
158 JSRuntime *runtimeFromMainThread() { |
|
159 JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); |
|
160 return runtime_; |
|
161 } |
|
162 |
|
163 // Note: Unrestricted access to the zone's runtime from an arbitrary |
|
164 // thread can easily lead to races. Use this method very carefully. |
|
165 JSRuntime *runtimeFromAnyThread() const { |
|
166 return runtime_; |
|
167 } |
|
168 |
|
169 /* |
|
170 * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or |
|
171 * (b) the compartment's global has been collected. The latter can happen |
|
172 * if e.g. a string in a compartment is rooted but no object is, and thus |
|
173 * the global isn't rooted, and thus the global can be finalized while the |
|
174 * compartment lives on. |
|
175 * |
|
176 * In contrast, JSObject::global() is infallible because marking a JSObject |
|
177 * always marks its global as well. |
|
178 * TODO: add infallible JSScript::global() |
|
179 */ |
|
180 inline js::GlobalObject *maybeGlobal() const; |
|
181 |
|
182 inline void initGlobal(js::GlobalObject &global); |
|
183 |
|
184 public: |
|
185 /* |
|
186 * Moves all data from the allocator |workerAllocator|, which was |
|
187 * in use by a parallel worker, into the compartment's main |
|
188 * allocator. This is used at the end of a parallel section. |
|
189 */ |
|
190 void adoptWorkerAllocator(js::Allocator *workerAllocator); |
|
191 |
|
192 bool activeAnalysis; |
|
193 |
|
194 /* Type information about the scripts and objects in this compartment. */ |
|
195 js::types::TypeCompartment types; |
|
196 |
|
197 void *data; |
|
198 |
|
199 private: |
|
200 js::ObjectMetadataCallback objectMetadataCallback; |
|
201 |
|
202 js::SavedStacks savedStacks_; |
|
203 |
|
204 js::WrapperMap crossCompartmentWrappers; |
|
205 |
|
206 public: |
|
207 /* Last time at which an animation was played for a global in this compartment. */ |
|
208 int64_t lastAnimationTime; |
|
209 |
|
210 js::RegExpCompartment regExps; |
|
211 |
|
212 /* |
|
213 * For generational GC, record whether a write barrier has added this |
|
214 * compartment's global to the store buffer since the last minor GC. |
|
215 * |
|
216 * This is used to avoid adding it to the store buffer on every write, which |
|
217 * can quickly fill the buffer and also cause performance problems. |
|
218 */ |
|
219 bool globalWriteBarriered; |
|
220 |
|
221 public: |
|
222 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, |
|
223 size_t *tiAllocationSiteTables, |
|
224 size_t *tiArrayTypeTables, |
|
225 size_t *tiObjectTypeTables, |
|
226 size_t *compartmentObject, |
|
227 size_t *shapesCompartmentTables, |
|
228 size_t *crossCompartmentWrappers, |
|
229 size_t *regexpCompartment, |
|
230 size_t *debuggeesSet, |
|
231 size_t *savedStacksSet); |
|
232 |
|
233 /* |
|
234 * Shared scope property tree, and arena-pool for allocating its nodes. |
|
235 */ |
|
236 js::PropertyTree propertyTree; |
|
237 |
|
238 /* Set of all unowned base shapes in the compartment. */ |
|
239 js::BaseShapeSet baseShapes; |
|
240 void sweepBaseShapeTable(); |
|
241 |
|
242 /* Set of initial shapes in the compartment. */ |
|
243 js::InitialShapeSet initialShapes; |
|
244 void sweepInitialShapeTable(); |
|
245 |
|
246 /* Set of default 'new' or lazy types in the compartment. */ |
|
247 js::types::TypeObjectWithNewScriptSet newTypeObjects; |
|
248 js::types::TypeObjectWithNewScriptSet lazyTypeObjects; |
|
249 void sweepNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table); |
|
250 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL) |
|
251 void checkNewTypeObjectTableAfterMovingGC(); |
|
252 void checkInitialShapesTableAfterMovingGC(); |
|
253 void checkWrapperMapAfterMovingGC(); |
|
254 #endif |
|
255 |
|
256 /* |
|
257 * Hash table of all manually call site-cloned functions from within |
|
258 * self-hosted code. Cloning according to call site provides extra |
|
259 * sensitivity for type specialization and inlining. |
|
260 */ |
|
261 js::CallsiteCloneTable callsiteClones; |
|
262 void sweepCallsiteClones(); |
|
263 |
|
264 /* |
|
265 * Lazily initialized script source object to use for scripts cloned |
|
266 * from the self-hosting global. |
|
267 */ |
|
268 js::ReadBarriered<js::ScriptSourceObject> selfHostingScriptSource; |
|
269 |
|
270 /* During GC, stores the index of this compartment in rt->compartments. */ |
|
271 unsigned gcIndex; |
|
272 |
|
273 /* |
|
274 * During GC, stores the head of a list of incoming pointers from gray cells. |
|
275 * |
|
276 * The objects in the list are either cross-compartment wrappers, or |
|
277 * debugger wrapper objects. The list link is either in the second extra |
|
278 * slot for the former, or a special slot for the latter. |
|
279 */ |
|
280 JSObject *gcIncomingGrayPointers; |
|
281 |
|
282 /* During GC, list of live array buffers with >1 view accumulated during tracing. */ |
|
283 js::ArrayBufferVector gcLiveArrayBuffers; |
|
284 |
|
285 /* Linked list of live weakmaps in this compartment. */ |
|
286 js::WeakMapBase *gcWeakMapList; |
|
287 |
|
288 private: |
|
289 enum { |
|
290 DebugFromC = 1 << 0, |
|
291 DebugFromJS = 1 << 1, |
|
292 DebugNeedDelazification = 1 << 2 |
|
293 }; |
|
294 |
|
295 static const unsigned DebugModeFromMask = DebugFromC | DebugFromJS; |
|
296 |
|
297 unsigned debugModeBits; // see debugMode() below |
|
298 |
|
299 public: |
|
300 JSCompartment(JS::Zone *zone, const JS::CompartmentOptions &options); |
|
301 ~JSCompartment(); |
|
302 |
|
303 bool init(JSContext *cx); |
|
304 |
|
305 /* Mark cross-compartment wrappers. */ |
|
306 void markCrossCompartmentWrappers(JSTracer *trc); |
|
307 |
|
308 inline bool wrap(JSContext *cx, JS::MutableHandleValue vp, |
|
309 JS::HandleObject existing = js::NullPtr()); |
|
310 |
|
311 bool wrap(JSContext *cx, JSString **strp); |
|
312 bool wrap(JSContext *cx, js::HeapPtrString *strp); |
|
313 bool wrap(JSContext *cx, JS::MutableHandleObject obj, |
|
314 JS::HandleObject existingArg = js::NullPtr()); |
|
315 bool wrapId(JSContext *cx, jsid *idp); |
|
316 bool wrap(JSContext *cx, js::PropertyOp *op); |
|
317 bool wrap(JSContext *cx, js::StrictPropertyOp *op); |
|
318 bool wrap(JSContext *cx, JS::MutableHandle<js::PropertyDescriptor> desc); |
|
319 bool wrap(JSContext *cx, js::AutoIdVector &props); |
|
320 |
|
321 bool putWrapper(JSContext *cx, const js::CrossCompartmentKey& wrapped, const js::Value& wrapper); |
|
322 |
|
323 js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) { |
|
324 return crossCompartmentWrappers.lookup(wrapped); |
|
325 } |
|
326 |
|
327 void removeWrapper(js::WrapperMap::Ptr p) { |
|
328 crossCompartmentWrappers.remove(p); |
|
329 } |
|
330 |
|
331 struct WrapperEnum : public js::WrapperMap::Enum { |
|
332 WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {} |
|
333 }; |
|
334 |
|
335 void trace(JSTracer *trc); |
|
336 void markRoots(JSTracer *trc); |
|
337 bool isDiscardingJitCode(JSTracer *trc); |
|
338 void sweep(js::FreeOp *fop, bool releaseTypes); |
|
339 void sweepCrossCompartmentWrappers(); |
|
340 void purge(); |
|
341 void clearTables(); |
|
342 |
|
343 bool hasObjectMetadataCallback() const { return objectMetadataCallback; } |
|
344 void setObjectMetadataCallback(js::ObjectMetadataCallback callback); |
|
345 bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const { |
|
346 return objectMetadataCallback(cx, obj); |
|
347 } |
|
348 |
|
349 js::SavedStacks &savedStacks() { return savedStacks_; } |
|
350 |
|
351 void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder); |
|
352 |
|
353 js::DtoaCache dtoaCache; |
|
354 |
|
355 /* Random number generator state, used by jsmath.cpp. */ |
|
356 uint64_t rngState; |
|
357 |
|
358 private: |
|
359 /* |
|
360 * Weak reference to each global in this compartment that is a debuggee. |
|
361 * Each global has its own list of debuggers. |
|
362 */ |
|
363 js::GlobalObjectSet debuggees; |
|
364 |
|
365 private: |
|
366 JSCompartment *thisForCtor() { return this; } |
|
367 |
|
368 public: |
|
369 /* |
|
370 * There are dueling APIs for debug mode. It can be enabled or disabled via |
|
371 * JS_SetDebugModeForCompartment. It is automatically enabled and disabled |
|
372 * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set |
|
373 * if the C API wants debug mode and the DebugFromJS bit set if debuggees |
|
374 * is non-empty. |
|
375 * |
|
376 * When toggling on, DebugNeedDelazification is set to signal that |
|
377 * Debugger methods which depend on seeing all scripts (like findScripts) |
|
378 * need to delazify the scripts in the compartment first. |
|
379 */ |
|
380 bool debugMode() const { |
|
381 return !!(debugModeBits & DebugModeFromMask); |
|
382 } |
|
383 |
|
384 /* True if any scripts from this compartment are on the JS stack. */ |
|
385 bool hasScriptsOnStack(); |
|
386 |
|
387 /* |
|
388 * Schedule the compartment to be delazified. Called from |
|
389 * LazyScript::Create. |
|
390 */ |
|
391 void scheduleDelazificationForDebugMode() { |
|
392 debugModeBits |= DebugNeedDelazification; |
|
393 } |
|
394 |
|
395 /* |
|
396 * If we scheduled delazification for turning on debug mode, delazify all |
|
397 * scripts. |
|
398 */ |
|
399 bool ensureDelazifyScriptsForDebugMode(JSContext *cx); |
|
400 |
|
401 private: |
|
402 |
|
403 /* This is called only when debugMode() has just toggled. */ |
|
404 bool updateJITForDebugMode(JSContext *maybecx, js::AutoDebugModeInvalidation &invalidate); |
|
405 |
|
406 public: |
|
407 js::GlobalObjectSet &getDebuggees() { return debuggees; } |
|
408 bool addDebuggee(JSContext *cx, js::GlobalObject *global); |
|
409 bool addDebuggee(JSContext *cx, js::GlobalObject *global, |
|
410 js::AutoDebugModeInvalidation &invalidate); |
|
411 bool removeDebuggee(JSContext *cx, js::GlobalObject *global, |
|
412 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); |
|
413 bool removeDebuggee(JSContext *cx, js::GlobalObject *global, |
|
414 js::AutoDebugModeInvalidation &invalidate, |
|
415 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); |
|
416 void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global, |
|
417 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); |
|
418 void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global, |
|
419 js::AutoDebugModeInvalidation &invalidate, |
|
420 js::GlobalObjectSet::Enum *debuggeesEnum = nullptr); |
|
421 bool setDebugModeFromC(JSContext *cx, bool b, |
|
422 js::AutoDebugModeInvalidation &invalidate); |
|
423 |
|
424 void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JS::HandleObject handler); |
|
425 void clearTraps(js::FreeOp *fop); |
|
426 |
|
427 private: |
|
428 void sweepBreakpoints(js::FreeOp *fop); |
|
429 |
|
430 public: |
|
431 js::WatchpointMap *watchpointMap; |
|
432 |
|
433 js::ScriptCountsMap *scriptCountsMap; |
|
434 |
|
435 js::DebugScriptMap *debugScriptMap; |
|
436 |
|
437 /* Bookkeeping information for debug scope objects. */ |
|
438 js::DebugScopes *debugScopes; |
|
439 |
|
440 /* |
|
441 * List of potentially active iterators that may need deleted property |
|
442 * suppression. |
|
443 */ |
|
444 js::NativeIterator *enumerators; |
|
445 |
|
446 /* Used by memory reporters and invalid otherwise. */ |
|
447 void *compartmentStats; |
|
448 |
|
449 #ifdef JS_ION |
|
450 private: |
|
451 js::jit::JitCompartment *jitCompartment_; |
|
452 |
|
453 public: |
|
454 bool ensureJitCompartmentExists(JSContext *cx); |
|
455 js::jit::JitCompartment *jitCompartment() { |
|
456 return jitCompartment_; |
|
457 } |
|
458 #endif |
|
459 }; |
|
460 |
|
461 inline bool |
|
462 JSRuntime::isAtomsZone(JS::Zone *zone) |
|
463 { |
|
464 return zone == atomsCompartment_->zone(); |
|
465 } |
|
466 |
|
467 // For use when changing the debug mode flag on one or more compartments. |
|
468 // Invalidate and discard JIT code since debug mode breaks JIT assumptions. |
|
469 // |
|
470 // AutoDebugModeInvalidation has two modes: compartment or zone |
|
471 // invalidation. While it is correct to always use compartment invalidation, |
|
472 // if you know ahead of time you need to invalidate a whole zone, it is faster |
|
473 // to invalidate the zone. |
|
474 // |
|
475 // Compartment invalidation only invalidates scripts belonging to that |
|
476 // compartment. |
|
477 // |
|
478 // Zone invalidation invalidates all scripts belonging to non-special |
|
479 // (i.e. those with principals) compartments of the zone. |
|
480 // |
|
481 // FIXME: Remove entirely once bug 716647 lands. |
|
482 // |
|
483 class js::AutoDebugModeInvalidation |
|
484 { |
|
485 JSCompartment *comp_; |
|
486 JS::Zone *zone_; |
|
487 |
|
488 enum { |
|
489 NoNeed = 0, |
|
490 ToggledOn = 1, |
|
491 ToggledOff = 2 |
|
492 } needInvalidation_; |
|
493 |
|
494 public: |
|
495 explicit AutoDebugModeInvalidation(JSCompartment *comp) |
|
496 : comp_(comp), zone_(nullptr), needInvalidation_(NoNeed) |
|
497 { } |
|
498 |
|
499 explicit AutoDebugModeInvalidation(JS::Zone *zone) |
|
500 : comp_(nullptr), zone_(zone), needInvalidation_(NoNeed) |
|
501 { } |
|
502 |
|
503 #ifdef JS_ION |
|
504 ~AutoDebugModeInvalidation(); |
|
505 #else |
|
506 ~AutoDebugModeInvalidation() { } |
|
507 #endif |
|
508 |
|
509 bool isFor(JSCompartment *comp) { |
|
510 if (comp_) |
|
511 return comp == comp_; |
|
512 return comp->zone() == zone_; |
|
513 } |
|
514 |
|
515 void scheduleInvalidation(bool debugMode) { |
|
516 // If we are scheduling invalidation for multiple compartments, they |
|
517 // must all agree on the toggle. This is so we can decide if we need |
|
518 // to invalidate on-stack scripts. |
|
519 MOZ_ASSERT_IF(needInvalidation_ != NoNeed, |
|
520 needInvalidation_ == (debugMode ? ToggledOn : ToggledOff)); |
|
521 needInvalidation_ = debugMode ? ToggledOn : ToggledOff; |
|
522 } |
|
523 }; |
|
524 |
|
525 namespace js { |
|
526 |
|
527 inline js::Handle<js::GlobalObject*> |
|
528 ExclusiveContext::global() const |
|
529 { |
|
530 /* |
|
531 * It's safe to use |unsafeGet()| here because any compartment that is |
|
532 * on-stack will be marked automatically, so there's no need for a read |
|
533 * barrier on it. Once the compartment is popped, the handle is no longer |
|
534 * safe to use. |
|
535 */ |
|
536 MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first"); |
|
537 return Handle<GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet()); |
|
538 } |
|
539 |
|
540 class AssertCompartmentUnchanged |
|
541 { |
|
542 public: |
|
543 AssertCompartmentUnchanged(JSContext *cx |
|
544 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
545 : cx(cx), oldCompartment(cx->compartment()) |
|
546 { |
|
547 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
548 } |
|
549 |
|
550 ~AssertCompartmentUnchanged() { |
|
551 JS_ASSERT(cx->compartment() == oldCompartment); |
|
552 } |
|
553 |
|
554 protected: |
|
555 JSContext * const cx; |
|
556 JSCompartment * const oldCompartment; |
|
557 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
558 }; |
|
559 |
|
560 class AutoCompartment |
|
561 { |
|
562 ExclusiveContext * const cx_; |
|
563 JSCompartment * const origin_; |
|
564 |
|
565 public: |
|
566 inline AutoCompartment(ExclusiveContext *cx, JSObject *target); |
|
567 inline AutoCompartment(ExclusiveContext *cx, JSCompartment *target); |
|
568 inline ~AutoCompartment(); |
|
569 |
|
570 ExclusiveContext *context() const { return cx_; } |
|
571 JSCompartment *origin() const { return origin_; } |
|
572 |
|
573 private: |
|
574 AutoCompartment(const AutoCompartment &) MOZ_DELETE; |
|
575 AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE; |
|
576 }; |
|
577 |
|
578 /* |
|
579 * Use this to change the behavior of an AutoCompartment slightly on error. If |
|
580 * the exception happens to be an Error object, copy it to the origin compartment |
|
581 * instead of wrapping it. |
|
582 */ |
|
583 class ErrorCopier |
|
584 { |
|
585 mozilla::Maybe<AutoCompartment> ∾ |
|
586 RootedObject scope; |
|
587 |
|
588 public: |
|
589 ErrorCopier(mozilla::Maybe<AutoCompartment> &ac, JSObject *scope) |
|
590 : ac(ac), scope(ac.ref().context(), scope) {} |
|
591 ~ErrorCopier(); |
|
592 }; |
|
593 |
|
594 /* |
|
595 * AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that |
|
596 * are obtained from the cross-compartment map. However, these classes should |
|
597 * not be used if the wrapper will escape. For example, it should not be stored |
|
598 * in the heap. |
|
599 * |
|
600 * The AutoWrapper rooters are different from other autorooters because their |
|
601 * wrappers are marked on every GC slice rather than just the first one. If |
|
602 * there's some wrapper that we want to use temporarily without causing it to be |
|
603 * marked, we can use these AutoWrapper classes. If we get unlucky and a GC |
|
604 * slice runs during the code using the wrapper, the GC will mark the wrapper so |
|
605 * that it doesn't get swept out from under us. Otherwise, the wrapper needn't |
|
606 * be marked. This is useful in functions like JS_TransplantObject that |
|
607 * manipulate wrappers in compartments that may no longer be alive. |
|
608 */ |
|
609 |
|
610 /* |
|
611 * This class stores the data for AutoWrapperVector and AutoWrapperRooter. It |
|
612 * should not be used in any other situations. |
|
613 */ |
|
614 struct WrapperValue |
|
615 { |
|
616 /* |
|
617 * We use unsafeGet() in the constructors to avoid invoking a read barrier |
|
618 * on the wrapper, which may be dead (see the comment about bug 803376 in |
|
619 * jsgc.cpp regarding this). If there is an incremental GC while the wrapper |
|
620 * is in use, the AutoWrapper rooter will ensure the wrapper gets marked. |
|
621 */ |
|
622 explicit WrapperValue(const WrapperMap::Ptr &ptr) |
|
623 : value(*ptr->value().unsafeGet()) |
|
624 {} |
|
625 |
|
626 explicit WrapperValue(const WrapperMap::Enum &e) |
|
627 : value(*e.front().value().unsafeGet()) |
|
628 {} |
|
629 |
|
630 Value &get() { return value; } |
|
631 Value get() const { return value; } |
|
632 operator const Value &() const { return value; } |
|
633 JSObject &toObject() const { return value.toObject(); } |
|
634 |
|
635 private: |
|
636 Value value; |
|
637 }; |
|
638 |
|
639 class AutoWrapperVector : public AutoVectorRooter<WrapperValue> |
|
640 { |
|
641 public: |
|
642 explicit AutoWrapperVector(JSContext *cx |
|
643 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
644 : AutoVectorRooter<WrapperValue>(cx, WRAPVECTOR) |
|
645 { |
|
646 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
647 } |
|
648 |
|
649 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
650 }; |
|
651 |
|
652 class AutoWrapperRooter : private AutoGCRooter { |
|
653 public: |
|
654 AutoWrapperRooter(JSContext *cx, WrapperValue v |
|
655 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
656 : AutoGCRooter(cx, WRAPPER), value(v) |
|
657 { |
|
658 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
659 } |
|
660 |
|
661 operator JSObject *() const { |
|
662 return value.get().toObjectOrNull(); |
|
663 } |
|
664 |
|
665 friend void AutoGCRooter::trace(JSTracer *trc); |
|
666 |
|
667 private: |
|
668 WrapperValue value; |
|
669 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
670 }; |
|
671 |
|
672 } /* namespace js */ |
|
673 |
|
674 #endif /* jscompartment_h */ |