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 10385 : static size_t getIndex(uint32_t key) {
91 10385 : return size_t(key) % SIZE;
92 : }
93 :
94 : public:
95 : /* Native iterator most recently started. */
96 : JSObject *last;
97 :
98 45576 : NativeIterCache()
99 45576 : : last(NULL) {
100 45576 : PodArrayZero(data);
101 45576 : }
102 :
103 127756 : void purge() {
104 127756 : PodArrayZero(data);
105 127756 : last = NULL;
106 127756 : }
107 :
108 8710 : JSObject *get(uint32_t key) const {
109 8710 : return data[getIndex(key)];
110 : }
111 :
112 1675 : void set(uint32_t key, JSObject *iterobj) {
113 1675 : data[getIndex(key)] = iterobj;
114 1675 : }
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 45576 : DtoaCache() : s(NULL) {}
132 127756 : void purge() { s = NULL; }
133 :
134 45482624 : JSFixedString *lookup(int base, double d) {
135 45482624 : return this->s && base == this->base && d == this->d ? this->s : NULL;
136 : }
137 :
138 42726170 : void cache(int base, double d, JSFixedString *s) {
139 42726170 : this->base = base;
140 42726170 : this->d = d;
141 42726170 : this->s = s;
142 42726170 : }
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 1286209 : static HashNumber hash(const char *l) { return JS_HashString(l); }
156 1207673 : static bool match(const ScriptFilenameEntry *e, const char *l) {
157 1207673 : 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 152367 : static HashNumber hash(Value key) {
173 152367 : uint64_t bits = JSVAL_TO_IMPL(key).asBits;
174 152367 : return uint32_t(bits) ^ uint32_t(bits >> 32);
175 : }
176 :
177 47129 : 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 935052583 : bool needsBarrier() {
198 935052583 : return needsBarrier_;
199 : }
200 :
201 200076 : js::GCMarker *barrierTracer() {
202 200076 : JS_ASSERT(needsBarrier_);
203 200076 : return &rt->gcMarker;
204 : }
205 :
206 : size_t gcBytes;
207 : size_t gcTriggerBytes;
208 : size_t gcMaxMallocBytes;
209 :
210 : bool hold;
211 : bool isSystemCompartment;
212 :
213 : /*
214 : * Pool for analysis and intermediate type information in this compartment.
215 : * Cleared on every GC, unless the GC happens during analysis (indicated
216 : * by activeAnalysis, which is implied by activeInference).
217 : */
218 : static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 128 * 1024;
219 : js::LifoAlloc typeLifoAlloc;
220 : bool activeAnalysis;
221 : bool activeInference;
222 :
223 : /* Type information about the scripts and objects in this compartment. */
224 : js::types::TypeCompartment types;
225 :
226 : public:
227 : /* Hashed lists of scripts created by eval to garbage-collect. */
228 : JSScript *evalCache[JS_EVAL_CACHE_SIZE];
229 :
230 : void *data;
231 : bool active; // GC flag, whether there are active frames
232 : js::WrapperMap crossCompartmentWrappers;
233 :
234 : #ifdef JS_METHODJIT
235 : private:
236 : /* This is created lazily because many compartments don't need it. */
237 : js::mjit::JaegerCompartment *jaegerCompartment_;
238 : /*
239 : * This function is here so that xpconnect/src/xpcjsruntime.cpp doesn't
240 : * need to see the declaration of JaegerCompartment, which would require
241 : * #including MethodJIT.h into xpconnect/src/xpcjsruntime.cpp, which is
242 : * difficult due to reasons explained in bug 483677.
243 : */
244 : public:
245 6319421 : bool hasJaegerCompartment() {
246 6319421 : return !!jaegerCompartment_;
247 : }
248 :
249 18494095 : js::mjit::JaegerCompartment *jaegerCompartment() const {
250 18494095 : JS_ASSERT(jaegerCompartment_);
251 18494095 : return jaegerCompartment_;
252 : }
253 :
254 : bool ensureJaegerCompartmentExists(JSContext *cx);
255 :
256 : size_t sizeOfMjitCode() const;
257 : #endif
258 :
259 : js::RegExpCompartment regExps;
260 :
261 : size_t sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf);
262 : void sizeOfTypeInferenceData(JS::TypeInferenceSizes *stats, JSMallocSizeOfFun mallocSizeOf);
263 :
264 : /*
265 : * Shared scope property tree, and arena-pool for allocating its nodes.
266 : */
267 : js::PropertyTree propertyTree;
268 :
269 : /* Set of all unowned base shapes in the compartment. */
270 : js::BaseShapeSet baseShapes;
271 : void sweepBaseShapeTable(JSContext *cx);
272 :
273 : /* Set of initial shapes in the compartment. */
274 : js::InitialShapeSet initialShapes;
275 : void sweepInitialShapeTable(JSContext *cx);
276 :
277 : /* Set of default 'new' or lazy types in the compartment. */
278 : js::types::TypeObjectSet newTypeObjects;
279 : js::types::TypeObjectSet lazyTypeObjects;
280 : void sweepNewTypeObjectTable(JSContext *cx, js::types::TypeObjectSet &table);
281 :
282 : js::types::TypeObject *emptyTypeObject;
283 :
284 : /* Get the default 'new' type for objects with a NULL prototype. */
285 : inline js::types::TypeObject *getEmptyType(JSContext *cx);
286 :
287 : js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
288 :
289 : /* Cache to speed up object creation. */
290 : js::NewObjectCache newObjectCache;
291 :
292 : /*
293 : * Keeps track of the total number of malloc bytes connected to a
294 : * compartment's GC things. This counter should be used in preference to
295 : * gcMallocBytes. These counters affect collection in the same way as
296 : * gcBytes and gcTriggerBytes.
297 : */
298 : size_t gcMallocAndFreeBytes;
299 : size_t gcTriggerMallocAndFreeBytes;
300 :
301 : private:
302 : /*
303 : * Malloc counter to measure memory pressure for GC scheduling. It runs from
304 : * gcMaxMallocBytes down to zero. This counter should be used only when it's
305 : * not possible to know the size of a free.
306 : */
307 : ptrdiff_t gcMallocBytes;
308 :
309 : enum { DebugFromC = 1, DebugFromJS = 2 };
310 :
311 : unsigned debugModeBits; // see debugMode() below
312 :
313 : public:
314 : js::NativeIterCache nativeIterCache;
315 :
316 : typedef js::Maybe<js::ToSourceCache> LazyToSourceCache;
317 : LazyToSourceCache toSourceCache;
318 :
319 : js::ScriptFilenameTable scriptFilenameTable;
320 :
321 : JSCompartment(JSRuntime *rt);
322 : ~JSCompartment();
323 :
324 : bool init(JSContext *cx);
325 :
326 : /* Mark cross-compartment wrappers. */
327 : void markCrossCompartmentWrappers(JSTracer *trc);
328 :
329 : bool wrap(JSContext *cx, js::Value *vp);
330 : bool wrap(JSContext *cx, JSString **strp);
331 : bool wrap(JSContext *cx, js::HeapPtrString *strp);
332 : bool wrap(JSContext *cx, JSObject **objp);
333 : bool wrapId(JSContext *cx, jsid *idp);
334 : bool wrap(JSContext *cx, js::PropertyOp *op);
335 : bool wrap(JSContext *cx, js::StrictPropertyOp *op);
336 : bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
337 : bool wrap(JSContext *cx, js::AutoIdVector &props);
338 :
339 : void markTypes(JSTracer *trc);
340 : void discardJitCode(JSContext *cx);
341 : void sweep(JSContext *cx, bool releaseTypes);
342 : void purge();
343 :
344 : void setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, js::JSGCInvocationKind gckind);
345 : void reduceGCTriggerBytes(size_t amount);
346 :
347 : void resetGCMallocBytes();
348 : void setGCMaxMallocBytes(size_t value);
349 33565533 : void updateMallocCounter(size_t nbytes) {
350 33565533 : ptrdiff_t oldCount = gcMallocBytes;
351 33565533 : ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes);
352 33565533 : gcMallocBytes = newCount;
353 33565533 : if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
354 118 : onTooMuchMalloc();
355 33565533 : }
356 :
357 : void onTooMuchMalloc();
358 :
359 0 : void mallocInCompartment(size_t nbytes) {
360 0 : gcMallocAndFreeBytes += nbytes;
361 0 : }
362 :
363 0 : void freeInCompartment(size_t nbytes) {
364 0 : JS_ASSERT(gcMallocAndFreeBytes >= nbytes);
365 0 : gcMallocAndFreeBytes -= nbytes;
366 0 : }
367 :
368 : js::DtoaCache dtoaCache;
369 :
370 : private:
371 : js::MathCache *mathCache;
372 :
373 : js::MathCache *allocMathCache(JSContext *cx);
374 :
375 : /*
376 : * Weak reference to each global in this compartment that is a debuggee.
377 : * Each global has its own list of debuggers.
378 : */
379 : js::GlobalObjectSet debuggees;
380 :
381 : private:
382 45576 : JSCompartment *thisForCtor() { return this; }
383 :
384 : public:
385 2850867 : js::MathCache *getMathCache(JSContext *cx) {
386 2850867 : return mathCache ? mathCache : allocMathCache(cx);
387 : }
388 :
389 : /*
390 : * There are dueling APIs for debug mode. It can be enabled or disabled via
391 : * JS_SetDebugModeForCompartment. It is automatically enabled and disabled
392 : * by Debugger objects. Therefore debugModeBits has the DebugFromC bit set
393 : * if the C API wants debug mode and the DebugFromJS bit set if debuggees
394 : * is non-empty.
395 : */
396 33199406 : bool debugMode() const { return !!debugModeBits; }
397 :
398 : /* True if any scripts from this compartment are on the JS stack. */
399 : bool hasScriptsOnStack();
400 :
401 : private:
402 : /* This is called only when debugMode() has just toggled. */
403 : void updateForDebugMode(JSContext *cx);
404 :
405 : public:
406 34488901 : js::GlobalObjectSet &getDebuggees() { return debuggees; }
407 : bool addDebuggee(JSContext *cx, js::GlobalObject *global);
408 : void removeDebuggee(JSContext *cx, js::GlobalObject *global,
409 : js::GlobalObjectSet::Enum *debuggeesEnum = NULL);
410 : bool setDebugModeFromC(JSContext *cx, bool b);
411 :
412 : void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler);
413 : void clearTraps(JSContext *cx);
414 :
415 : private:
416 : void sweepBreakpoints(JSContext *cx);
417 :
418 : public:
419 : js::WatchpointMap *watchpointMap;
420 : };
421 :
422 : #define JS_PROPERTY_TREE(cx) ((cx)->compartment->propertyTree)
423 :
424 : namespace js {
425 : static inline MathCache *
426 2850867 : GetMathCache(JSContext *cx)
427 : {
428 2850867 : return cx->compartment->getMathCache(cx);
429 : }
430 : }
431 :
432 : inline void
433 19379916 : JSContext::setCompartment(JSCompartment *compartment)
434 : {
435 19379916 : this->compartment = compartment;
436 19379916 : this->inferenceEnabled = compartment ? compartment->types.inferenceEnabled : false;
437 19379916 : }
438 :
439 : #ifdef _MSC_VER
440 : #pragma warning(pop)
441 : #endif
442 :
443 : namespace js {
444 :
445 : class PreserveCompartment {
446 : protected:
447 : JSContext *cx;
448 : private:
449 : JSCompartment *oldCompartment;
450 : bool oldInferenceEnabled;
451 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
452 : public:
453 19057012 : PreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) {
454 19057012 : JS_GUARD_OBJECT_NOTIFIER_INIT;
455 19057012 : oldCompartment = cx->compartment;
456 19057012 : oldInferenceEnabled = cx->inferenceEnabled;
457 19057012 : }
458 :
459 38114024 : ~PreserveCompartment() {
460 : /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */
461 19057012 : cx->compartment = oldCompartment;
462 19057012 : cx->inferenceEnabled = oldInferenceEnabled;
463 19057012 : }
464 : };
465 :
466 19057012 : class SwitchToCompartment : public PreserveCompartment {
467 : public:
468 19057012 : SwitchToCompartment(JSContext *cx, JSCompartment *newCompartment
469 : JS_GUARD_OBJECT_NOTIFIER_PARAM)
470 19057012 : : PreserveCompartment(cx)
471 : {
472 19057012 : JS_GUARD_OBJECT_NOTIFIER_INIT;
473 19057012 : cx->setCompartment(newCompartment);
474 19057012 : }
475 :
476 : SwitchToCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM)
477 : : PreserveCompartment(cx)
478 : {
479 : JS_GUARD_OBJECT_NOTIFIER_INIT;
480 : cx->setCompartment(target->compartment());
481 : }
482 :
483 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
484 : };
485 :
486 : class AssertCompartmentUnchanged {
487 : protected:
488 : JSContext * const cx;
489 : JSCompartment * const oldCompartment;
490 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
491 : public:
492 4229345 : AssertCompartmentUnchanged(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM)
493 4229345 : : cx(cx), oldCompartment(cx->compartment) {
494 4229345 : JS_GUARD_OBJECT_NOTIFIER_INIT;
495 4229345 : }
496 :
497 8458690 : ~AssertCompartmentUnchanged() {
498 4229345 : JS_ASSERT(cx->compartment == oldCompartment);
499 4229345 : }
500 : };
501 :
502 : class AutoCompartment
503 : {
504 : public:
505 : JSContext * const context;
506 : JSCompartment * const origin;
507 : JSObject * const target;
508 : JSCompartment * const destination;
509 : private:
510 : Maybe<DummyFrameGuard> frame;
511 : bool entered;
512 :
513 : public:
514 : AutoCompartment(JSContext *cx, JSObject *target);
515 : ~AutoCompartment();
516 :
517 : bool enter();
518 : void leave();
519 :
520 : private:
521 : AutoCompartment(const AutoCompartment &) MOZ_DELETE;
522 : AutoCompartment & operator=(const AutoCompartment &) MOZ_DELETE;
523 : };
524 :
525 : /*
526 : * Use this to change the behavior of an AutoCompartment slightly on error. If
527 : * the exception happens to be an Error object, copy it to the origin compartment
528 : * instead of wrapping it.
529 : */
530 : class ErrorCopier
531 : {
532 : AutoCompartment ∾
533 : JSObject *scope;
534 :
535 : public:
536 9972 : ErrorCopier(AutoCompartment &ac, JSObject *scope) : ac(ac), scope(scope) {
537 9972 : JS_ASSERT(scope->compartment() == ac.origin);
538 9972 : }
539 : ~ErrorCopier();
540 : };
541 :
542 : class CompartmentsIter {
543 : private:
544 : JSCompartment **it, **end;
545 :
546 : public:
547 535717 : CompartmentsIter(JSRuntime *rt) {
548 535717 : it = rt->compartments.begin();
549 535717 : end = rt->compartments.end();
550 535717 : }
551 :
552 4301394 : bool done() const { return it == end; }
553 :
554 1188347 : void next() {
555 1188347 : JS_ASSERT(!done());
556 1188347 : it++;
557 1188347 : }
558 :
559 1388983 : JSCompartment *get() const {
560 1388983 : JS_ASSERT(!done());
561 1388983 : return *it;
562 : }
563 :
564 135074 : operator JSCompartment *() const { return get(); }
565 1234001 : JSCompartment *operator->() const { return get(); }
566 : };
567 :
568 : } /* namespace js */
569 :
570 : #endif /* jscompartment_h___ */
|