1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is SpiderMonkey code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 2010
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : *
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #ifndef jscompartment_h___
41 : #define jscompartment_h___
42 :
43 : #include "mozilla/Attributes.h"
44 :
45 : #include "jsclist.h"
46 : #include "jscntxt.h"
47 : #include "jsfun.h"
48 : #include "jsgc.h"
49 : #include "jsobj.h"
50 : #include "jsscope.h"
51 : #include "vm/GlobalObject.h"
52 : #include "vm/RegExpObject.h"
53 :
54 : #ifdef _MSC_VER
55 : #pragma warning(push)
56 : #pragma warning(disable:4251) /* Silence warning about JS_FRIEND_API and data members. */
57 : #endif
58 :
59 : namespace js {
60 :
61 : typedef HashMap<JSFunction *,
62 : JSString *,
63 : DefaultHasher<JSFunction *>,
64 : SystemAllocPolicy> ToSourceCache;
65 :
66 : namespace mjit {
67 : class JaegerCompartment;
68 : }
69 :
70 : /* Defined in jsapi.cpp */
71 : extern Class dummy_class;
72 :
73 : } /* namespace js */
74 :
75 : #ifndef JS_EVAL_CACHE_SHIFT
76 : # define JS_EVAL_CACHE_SHIFT 6
77 : #endif
78 :
79 : /* Number of buckets in the hash of eval scripts. */
80 : #define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT)
81 :
82 : namespace js {
83 :
84 : class NativeIterCache {
85 : static const size_t SIZE = size_t(1) << 8;
86 :
87 : /* Cached native iterators. */
88 : JSObject *data[SIZE];
89 :
90 10405 : static size_t getIndex(uint32_t key) {
91 10405 : return size_t(key) % SIZE;
92 : }
93 :
94 : public:
95 : /* Native iterator most recently started. */
96 : JSObject *last;
97 :
98 41285 : NativeIterCache()
99 41285 : : last(NULL) {
100 41285 : PodArrayZero(data);
101 41285 : }
102 :
103 89042 : void purge() {
104 89042 : PodArrayZero(data);
105 89042 : last = NULL;
106 89042 : }
107 :
108 8720 : JSObject *get(uint32_t key) const {
109 8720 : return data[getIndex(key)];
110 : }
111 :
112 1685 : void set(uint32_t key, JSObject *iterobj) {
113 1685 : data[getIndex(key)] = iterobj;
114 1685 : }
115 : };
116 :
117 : class MathCache;
118 :
119 : /*
120 : * A single-entry cache for some base-10 double-to-string conversions. This
121 : * helps date-format-xparb.js. It also avoids skewing the results for
122 : * v8-splay.js when measured by the SunSpider harness, where the splay tree
123 : * initialization (which includes many repeated double-to-string conversions)
124 : * is erroneously included in the measurement; see bug 562553.
125 : */
126 : class DtoaCache {
127 : double d;
128 : int base;
129 : JSFixedString *s; // if s==NULL, d and base are not valid
130 : public:
131 41285 : DtoaCache() : s(NULL) {}
132 89042 : void purge() { s = NULL; }
133 :
134 41750151 : JSFixedString *lookup(int base, double d) {
135 41750151 : return this->s && base == this->base && d == this->d ? this->s : NULL;
136 : }
137 :
138 39027547 : void cache(int base, double d, JSFixedString *s) {
139 39027547 : this->base = base;
140 39027547 : this->d = d;
141 39027547 : this->s = s;
142 39027547 : }
143 :
144 : };
145 :
146 : struct ScriptFilenameEntry
147 : {
148 : bool marked;
149 : char filename[1];
150 : };
151 :
152 : struct ScriptFilenameHasher
153 : {
154 : typedef const char *Lookup;
155 6038588 : static HashNumber hash(const char *l) { return JS_HashString(l); }
156 5977420 : static bool match(const ScriptFilenameEntry *e, const char *l) {
157 5977420 : return strcmp(e->filename, l) == 0;
158 : }
159 : };
160 :
161 : typedef HashSet<ScriptFilenameEntry *,
162 : ScriptFilenameHasher,
163 : SystemAllocPolicy> ScriptFilenameTable;
164 :
165 : /* If HashNumber grows, need to change WrapperHasher. */
166 : JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
167 :
168 : struct WrapperHasher
169 : {
170 : typedef Value Lookup;
171 :
172 156380 : static HashNumber hash(Value key) {
173 156380 : uint64_t bits = JSVAL_TO_IMPL(key).asBits;
174 156380 : return uint32_t(bits) ^ uint32_t(bits >> 32);
175 : }
176 :
177 66073 : static bool match(const Value &l, const Value &k) { return l == k; }
178 : };
179 :
180 : typedef HashMap<Value, ReadBarrieredValue, WrapperHasher, SystemAllocPolicy> WrapperMap;
181 :
182 : } /* namespace js */
183 :
184 : namespace JS {
185 : struct TypeInferenceSizes;
186 : }
187 :
188 : struct JSCompartment
189 : {
190 : JSRuntime *rt;
191 : JSPrincipals *principals;
192 :
193 : js::gc::ArenaLists arenas;
194 :
195 : bool needsBarrier_;
196 :
197 539854494 : bool needsBarrier() const {
198 539854494 : return needsBarrier_;
199 : }
200 :
201 152440 : js::GCMarker *barrierTracer() {
202 152440 : JS_ASSERT(needsBarrier_);
203 152440 : return &rt->gcMarker;
204 : }
205 :
206 : private:
207 : enum CompartmentGCState {
208 : NoGCScheduled,
209 : GCScheduled,
210 : GCRunning
211 : };
212 :
213 : CompartmentGCState gcState;
214 :
215 : public:
216 860227806 : bool isCollecting() const {
217 : /* Allow this if we're in the middle of an incremental GC. */
218 860227806 : if (rt->gcRunning) {
219 859839113 : return gcState == GCRunning;
220 : } else {
221 388693 : JS_ASSERT(gcState != GCRunning);
222 388693 : return needsBarrier();
223 : }
224 : }
225 :
226 : /*
227 : * If this returns true, all object tracing must be done with a GC marking
228 : * tracer.
229 : */
230 263204738 : bool requireGCTracer() const {
231 263204738 : return gcState == GCRunning;
232 : }
233 :
234 145068 : void setCollecting(bool collecting) {
235 145068 : JS_ASSERT(rt->gcRunning);
236 145068 : if (collecting)
237 83796 : gcState = GCRunning;
238 : else
239 61272 : gcState = NoGCScheduled;
240 145068 : }
241 :
242 181865 : void scheduleGC() {
243 181865 : JS_ASSERT(!rt->gcRunning);
244 181865 : JS_ASSERT(gcState != GCRunning);
245 181865 : gcState = GCScheduled;
246 181865 : }
247 :
248 192109 : bool isGCScheduled() const {
249 192109 : return gcState == GCScheduled;
250 : }
251 :
252 : size_t gcBytes;
253 : size_t gcTriggerBytes;
254 : size_t gcMaxMallocBytes;
255 :
256 : bool hold;
257 : bool isSystemCompartment;
258 :
259 : /*
260 : * Pool for analysis and intermediate type information in this compartment.
261 : * Cleared on every GC, unless the GC happens during analysis (indicated
262 : * by activeAnalysis, which is implied by activeInference).
263 : */
264 : static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 128 * 1024;
265 : js::LifoAlloc typeLifoAlloc;
266 : bool activeAnalysis;
267 : bool activeInference;
268 :
269 : /* Type information about the scripts and objects in this compartment. */
270 : js::types::TypeCompartment types;
271 :
272 : public:
273 : /* Hashed lists of scripts created by eval to garbage-collect. */
274 : JSScript *evalCache[JS_EVAL_CACHE_SIZE];
275 :
276 : void *data;
277 : bool active; // GC flag, whether there are active frames
278 : js::WrapperMap crossCompartmentWrappers;
279 :
280 : #ifdef JS_METHODJIT
281 : private:
282 : /* This is created lazily because many compartments don't need it. */
283 : js::mjit::JaegerCompartment *jaegerCompartment_;
284 : /*
285 : * This function is here so that xpconnect/src/xpcjsruntime.cpp doesn't
286 : * need to see the declaration of JaegerCompartment, which would require
287 : * #including MethodJIT.h into xpconnect/src/xpcjsruntime.cpp, which is
288 : * difficult due to reasons explained in bug 483677.
289 : */
290 : public:
291 1208590 : bool hasJaegerCompartment() {
292 1208590 : return !!jaegerCompartment_;
293 : }
294 :
295 4549189 : js::mjit::JaegerCompartment *jaegerCompartment() const {
296 4549189 : JS_ASSERT(jaegerCompartment_);
297 4549189 : return jaegerCompartment_;
298 : }
299 :
300 : bool ensureJaegerCompartmentExists(JSContext *cx);
301 :
302 : size_t sizeOfMjitCode() const;
303 : #endif
304 :
305 : js::RegExpCompartment regExps;
306 :
307 : size_t sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf);
308 : void sizeOfTypeInferenceData(JS::TypeInferenceSizes *stats, JSMallocSizeOfFun mallocSizeOf);
309 :
310 : /*
311 : * Shared scope property tree, and arena-pool for allocating its nodes.
312 : */
313 : js::PropertyTree propertyTree;
314 :
315 : /* Set of all unowned base shapes in the compartment. */
316 : js::BaseShapeSet baseShapes;
317 : void sweepBaseShapeTable();
318 :
319 : /* Set of initial shapes in the compartment. */
320 : js::InitialShapeSet initialShapes;
321 : void sweepInitialShapeTable();
322 :
323 : /* Set of default 'new' or lazy types in the compartment. */
324 : js::types::TypeObjectSet newTypeObjects;
325 : js::types::TypeObjectSet lazyTypeObjects;
326 : void sweepNewTypeObjectTable(js::types::TypeObjectSet &table);
327 :
328 : js::types::TypeObject *emptyTypeObject;
329 :
330 : /* Get the default 'new' type for objects with a NULL prototype. */
331 : inline js::types::TypeObject *getEmptyType(JSContext *cx);
332 :
333 : js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
334 :
335 : /* Cache to speed up object creation. */
336 : js::NewObjectCache newObjectCache;
337 :
338 : /*
339 : * Keeps track of the total number of malloc bytes connected to a
340 : * compartment's GC things. This counter should be used in preference to
341 : * gcMallocBytes. These counters affect collection in the same way as
342 : * gcBytes and gcTriggerBytes.
343 : */
344 : size_t gcMallocAndFreeBytes;
345 : size_t gcTriggerMallocAndFreeBytes;
346 :
347 : private:
348 : /*
349 : * Malloc counter to measure memory pressure for GC scheduling. It runs from
350 : * gcMaxMallocBytes down to zero. This counter should be used only when it's
351 : * not possible to know the size of a free.
352 : */
353 : ptrdiff_t gcMallocBytes;
354 :
355 : enum { DebugFromC = 1, DebugFromJS = 2 };
356 :
357 : unsigned debugModeBits; // see debugMode() below
358 :
359 : public:
360 : js::NativeIterCache nativeIterCache;
361 :
362 : typedef js::Maybe<js::ToSourceCache> LazyToSourceCache;
363 : LazyToSourceCache toSourceCache;
364 :
365 : js::ScriptFilenameTable scriptFilenameTable;
366 :
367 : JSCompartment(JSRuntime *rt);
368 : ~JSCompartment();
369 :
370 : bool init(JSContext *cx);
371 :
372 : /* Mark cross-compartment wrappers. */
373 : void markCrossCompartmentWrappers(JSTracer *trc);
374 :
375 : bool wrap(JSContext *cx, js::Value *vp);
376 : bool wrap(JSContext *cx, JSString **strp);
377 : bool wrap(JSContext *cx, js::HeapPtrString *strp);
378 : bool wrap(JSContext *cx, JSObject **objp);
379 : bool wrapId(JSContext *cx, jsid *idp);
380 : bool wrap(JSContext *cx, js::PropertyOp *op);
381 : bool wrap(JSContext *cx, js::StrictPropertyOp *op);
382 : bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
383 : bool wrap(JSContext *cx, js::AutoIdVector &props);
384 :
385 : void markTypes(JSTracer *trc);
386 : void discardJitCode(js::FreeOp *fop);
387 : void sweep(js::FreeOp *fop, bool releaseTypes);
388 : void purge();
389 :
390 : void setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, js::JSGCInvocationKind gckind);
391 : void reduceGCTriggerBytes(size_t amount);
392 :
393 : void resetGCMallocBytes();
394 : void setGCMaxMallocBytes(size_t value);
395 16143748 : void updateMallocCounter(size_t nbytes) {
396 16143748 : ptrdiff_t oldCount = gcMallocBytes;
397 16143748 : ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes);
398 16143748 : gcMallocBytes = newCount;
399 16143748 : if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
400 108 : onTooMuchMalloc();
401 16143748 : }
402 :
403 : void onTooMuchMalloc();
404 :
405 0 : void mallocInCompartment(size_t nbytes) {
406 0 : gcMallocAndFreeBytes += nbytes;
407 0 : }
408 :
409 0 : void freeInCompartment(size_t nbytes) {
410 0 : JS_ASSERT(gcMallocAndFreeBytes >= nbytes);
411 0 : gcMallocAndFreeBytes -= nbytes;
412 0 : }
413 :
414 : js::DtoaCache dtoaCache;
415 :
416 : private:
417 : js::MathCache *mathCache;
418 :
419 : js::MathCache *allocMathCache(JSContext *cx);
420 :
421 : /*
422 : * Weak reference to each global in this compartment that is a debuggee.
423 : * Each global has its own list of debuggers.
424 : */
425 : js::GlobalObjectSet debuggees;
426 :
427 : private:
428 41285 : JSCompartment *thisForCtor() { return this; }
429 :
430 : public:
431 2850876 : js::MathCache *getMathCache(JSContext *cx) {
432 2850876 : return mathCache ? mathCache : allocMathCache(cx);
433 : }
434 :
435 : /*
436 : * There are dueling APIs for debug mode. It can be enabled or disabled via
437 : * JS_SetDebugModeForCompartment. It is automatically enabled and disabled
438 : * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set
439 : * if the C API wants debug mode and the DebugFromJS bit set if debuggees
440 : * is non-empty.
441 : */
442 22530077 : bool debugMode() const { return !!debugModeBits; }
443 :
444 : /* True if any scripts from this compartment are on the JS stack. */
445 : bool hasScriptsOnStack();
446 :
447 : private:
448 : /* This is called only when debugMode() has just toggled. */
449 : void updateForDebugMode(js::FreeOp *fop);
450 :
451 : public:
452 28456831 : js::GlobalObjectSet &getDebuggees() { return debuggees; }
453 : bool addDebuggee(JSContext *cx, js::GlobalObject *global);
454 : void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
455 : js::GlobalObjectSet::Enum *debuggeesEnum = NULL);
456 : bool setDebugModeFromC(JSContext *cx, bool b);
457 :
458 : void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler);
459 : void clearTraps(JSContext *cx);
460 :
461 : private:
462 : void sweepBreakpoints(js::FreeOp *fop);
463 :
464 : public:
465 : js::WatchpointMap *watchpointMap;
466 : };
467 :
468 : #define JS_PROPERTY_TREE(cx) ((cx)->compartment->propertyTree)
469 :
470 : namespace js {
471 : static inline MathCache *
472 2850876 : GetMathCache(JSContext *cx)
473 : {
474 2850876 : return cx->compartment->getMathCache(cx);
475 : }
476 : }
477 :
478 : inline void
479 12139038 : JSContext::setCompartment(JSCompartment *compartment)
480 : {
481 12139038 : this->compartment = compartment;
482 12139038 : this->inferenceEnabled = compartment ? compartment->types.inferenceEnabled : false;
483 12139038 : }
484 :
485 : #ifdef _MSC_VER
486 : #pragma warning(pop)
487 : #endif
488 :
489 : namespace js {
490 :
491 : class PreserveCompartment {
492 : protected:
493 : JSContext *cx;
494 : private:
495 : JSCompartment *oldCompartment;
496 : bool oldInferenceEnabled;
497 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
498 : public:
499 11883986 : PreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) {
500 11883986 : JS_GUARD_OBJECT_NOTIFIER_INIT;
501 11883986 : oldCompartment = cx->compartment;
502 11883986 : oldInferenceEnabled = cx->inferenceEnabled;
503 11883986 : }
504 :
505 23767972 : ~PreserveCompartment() {
506 : /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */
507 11883986 : cx->compartment = oldCompartment;
508 11883986 : cx->inferenceEnabled = oldInferenceEnabled;
509 11883986 : }
510 : };
511 :
512 11883986 : class SwitchToCompartment : public PreserveCompartment {
513 : public:
514 11883986 : SwitchToCompartment(JSContext *cx, JSCompartment *newCompartment
515 : JS_GUARD_OBJECT_NOTIFIER_PARAM)
516 11883986 : : PreserveCompartment(cx)
517 : {
518 11883986 : JS_GUARD_OBJECT_NOTIFIER_INIT;
519 11883986 : cx->setCompartment(newCompartment);
520 11883986 : }
521 :
522 : SwitchToCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM)
523 : : PreserveCompartment(cx)
524 : {
525 : JS_GUARD_OBJECT_NOTIFIER_INIT;
526 : cx->setCompartment(target->compartment());
527 : }
528 :
529 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
530 : };
531 :
532 : class AssertCompartmentUnchanged {
533 : protected:
534 : JSContext * const cx;
535 : JSCompartment * const oldCompartment;
536 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
537 : public:
538 1189174 : AssertCompartmentUnchanged(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM)
539 1189174 : : cx(cx), oldCompartment(cx->compartment) {
540 1189174 : JS_GUARD_OBJECT_NOTIFIER_INIT;
541 1189174 : }
542 :
543 2378348 : ~AssertCompartmentUnchanged() {
544 1189174 : JS_ASSERT(cx->compartment == oldCompartment);
545 1189174 : }
546 : };
547 :
548 : class AutoCompartment
549 : {
550 : public:
551 : JSContext * const context;
552 : JSCompartment * const origin;
553 : JSObject * const target;
554 : JSCompartment * const destination;
555 : private:
556 : Maybe<DummyFrameGuard> frame;
557 : bool entered;
558 :
559 : public:
560 : AutoCompartment(JSContext *cx, JSObject *target);
561 : ~AutoCompartment();
562 :
563 : bool enter();
564 : void leave();
565 :
566 : private:
567 : AutoCompartment(const AutoCompartment &) MOZ_DELETE;
568 : AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE;
569 : };
570 :
571 : /*
572 : * Use this to change the behavior of an AutoCompartment slightly on error. If
573 : * the exception happens to be an Error object, copy it to the origin compartment
574 : * instead of wrapping it.
575 : */
576 : class ErrorCopier
577 : {
578 : AutoCompartment ∾
579 : JSObject *scope;
580 :
581 : public:
582 9981 : ErrorCopier(AutoCompartment &ac, JSObject *scope) : ac(ac), scope(scope) {
583 9981 : JS_ASSERT(scope->compartment() == ac.origin);
584 9981 : }
585 : ~ErrorCopier();
586 : };
587 :
588 : class CompartmentsIter {
589 : private:
590 : JSCompartment **it, **end;
591 :
592 : public:
593 745252 : CompartmentsIter(JSRuntime *rt) {
594 745252 : it = rt->compartments.begin();
595 745252 : end = rt->compartments.end();
596 745252 : }
597 :
598 5772921 : bool done() const { return it == end; }
599 :
600 1569153 : void next() {
601 1569153 : JS_ASSERT(!done());
602 1569153 : it++;
603 1569153 : }
604 :
605 1889363 : JSCompartment *get() const {
606 1889363 : JS_ASSERT(!done());
607 1889363 : return *it;
608 : }
609 :
610 95544 : operator JSCompartment *() const { return get(); }
611 1775058 : JSCompartment *operator->() const { return get(); }
612 : };
613 :
614 : } /* namespace js */
615 :
616 : #endif /* jscompartment_h___ */
|