1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Mozilla Foundation
22 : * Portions created by the Initial Developer are Copyright (C) 2010
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "jscntxt.h"
42 : #include "jscompartment.h"
43 : #include "jsgc.h"
44 : #include "jsgcmark.h"
45 : #include "jsiter.h"
46 : #include "jsmath.h"
47 : #include "jsproxy.h"
48 : #include "jsscope.h"
49 : #include "jswatchpoint.h"
50 : #include "jswrapper.h"
51 :
52 : #include "assembler/wtf/Platform.h"
53 : #include "js/MemoryMetrics.h"
54 : #include "methodjit/MethodJIT.h"
55 : #include "methodjit/PolyIC.h"
56 : #include "methodjit/MonoIC.h"
57 : #include "vm/Debugger.h"
58 : #include "yarr/BumpPointerAllocator.h"
59 :
60 : #include "jsgcinlines.h"
61 : #include "jsobjinlines.h"
62 : #include "jsscopeinlines.h"
63 :
64 : #if ENABLE_YARR_JIT
65 : #include "assembler/jit/ExecutableAllocator.h"
66 : #endif
67 :
68 : using namespace mozilla;
69 : using namespace js;
70 : using namespace js::gc;
71 :
72 41285 : JSCompartment::JSCompartment(JSRuntime *rt)
73 : : rt(rt),
74 : principals(NULL),
75 : needsBarrier_(false),
76 : gcState(NoGCScheduled),
77 : gcBytes(0),
78 : gcTriggerBytes(0),
79 : hold(false),
80 : typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
81 : data(NULL),
82 : active(false),
83 : #ifdef JS_METHODJIT
84 : jaegerCompartment_(NULL),
85 : #endif
86 : regExps(rt),
87 : propertyTree(thisForCtor()),
88 : emptyTypeObject(NULL),
89 : gcMallocAndFreeBytes(0),
90 : gcTriggerMallocAndFreeBytes(0),
91 : gcMallocBytes(0),
92 : debugModeBits(rt->debugMode ? DebugFromC : 0),
93 : mathCache(NULL),
94 41285 : watchpointMap(NULL)
95 : {
96 41285 : PodArrayZero(evalCache);
97 41285 : setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9);
98 41285 : }
99 :
100 82570 : JSCompartment::~JSCompartment()
101 : {
102 : /*
103 : * Even though all objects in the compartment are dead, we may have keep
104 : * some filenames around because of gcKeepAtoms.
105 : */
106 41285 : FreeScriptFilenames(this);
107 :
108 : #ifdef JS_METHODJIT
109 41285 : Foreground::delete_(jaegerCompartment_);
110 : #endif
111 :
112 41285 : Foreground::delete_(mathCache);
113 41285 : Foreground::delete_(watchpointMap);
114 :
115 : #ifdef DEBUG
116 2683525 : for (size_t i = 0; i < ArrayLength(evalCache); ++i)
117 2642240 : JS_ASSERT(!evalCache[i]);
118 : #endif
119 41285 : }
120 :
121 : bool
122 41285 : JSCompartment::init(JSContext *cx)
123 : {
124 41285 : activeAnalysis = activeInference = false;
125 41285 : types.init(cx);
126 :
127 41285 : newObjectCache.reset();
128 :
129 41285 : if (!crossCompartmentWrappers.init())
130 0 : return false;
131 :
132 41285 : if (!regExps.init(cx))
133 0 : return false;
134 :
135 41285 : if (!scriptFilenameTable.init())
136 0 : return false;
137 :
138 41285 : return debuggees.init();
139 : }
140 :
141 : #ifdef JS_METHODJIT
142 : bool
143 12505470 : JSCompartment::ensureJaegerCompartmentExists(JSContext *cx)
144 : {
145 12505470 : if (jaegerCompartment_)
146 12494480 : return true;
147 :
148 10990 : mjit::JaegerCompartment *jc = cx->new_<mjit::JaegerCompartment>();
149 10990 : if (!jc)
150 0 : return false;
151 10990 : if (!jc->Initialize(cx)) {
152 0 : cx->delete_(jc);
153 0 : return false;
154 : }
155 10990 : jaegerCompartment_ = jc;
156 10990 : return true;
157 : }
158 :
159 : size_t
160 0 : JSCompartment::sizeOfMjitCode() const
161 : {
162 0 : if (!jaegerCompartment_)
163 0 : return 0;
164 :
165 : size_t method, regexp, unused;
166 0 : jaegerCompartment_->execAlloc()->sizeOfCode(&method, ®exp, &unused);
167 0 : JS_ASSERT(regexp == 0);
168 0 : return method + unused;
169 : }
170 :
171 : #endif
172 :
173 : bool
174 6192726 : JSCompartment::wrap(JSContext *cx, Value *vp)
175 : {
176 6192726 : JS_ASSERT(cx->compartment == this);
177 :
178 6192726 : unsigned flags = 0;
179 :
180 6192726 : JS_CHECK_RECURSION(cx, return false);
181 :
182 : /* Only GC things have to be wrapped or copied. */
183 5728253 : if (!vp->isMarkable())
184 3900359 : return true;
185 :
186 1827894 : if (vp->isString()) {
187 892311 : JSString *str = vp->toString();
188 :
189 : /* If the string is already in this compartment, we are done. */
190 892311 : if (str->compartment() == this)
191 639 : return true;
192 :
193 : /* If the string is an atom, we don't have to copy. */
194 891672 : if (str->isAtom()) {
195 878312 : JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment);
196 878312 : return true;
197 : }
198 : }
199 :
200 : /*
201 : * Wrappers should really be parented to the wrapped parent of the wrapped
202 : * object, but in that case a wrapped global object would have a NULL
203 : * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
204 : * we parent all wrappers to the global object in their home compartment.
205 : * This loses us some transparency, and is generally very cheesy.
206 : */
207 : JSObject *global;
208 948943 : if (cx->hasfp()) {
209 929552 : global = &cx->fp()->scopeChain().global();
210 : } else {
211 19391 : global = JS_ObjectToInnerObject(cx, cx->globalObject);
212 19391 : if (!global)
213 0 : return false;
214 : }
215 :
216 : /* Unwrap incoming objects. */
217 948943 : if (vp->isObject()) {
218 935583 : JSObject *obj = &vp->toObject();
219 :
220 : /* If the object is already in this compartment, we are done. */
221 935583 : if (obj->compartment() == this)
222 741375 : return true;
223 :
224 : /* Translate StopIteration singleton. */
225 194208 : if (obj->isStopIteration())
226 0 : return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp);
227 :
228 : /* Don't unwrap an outer window proxy. */
229 194208 : if (!obj->getClass()->ext.innerObject) {
230 194202 : obj = UnwrapObject(&vp->toObject(), true, &flags);
231 194202 : vp->setObject(*obj);
232 194202 : if (obj->compartment() == this)
233 96343 : return true;
234 :
235 97859 : if (cx->runtime->preWrapObjectCallback) {
236 0 : obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
237 0 : if (!obj)
238 0 : return false;
239 : }
240 :
241 97859 : vp->setObject(*obj);
242 97859 : if (obj->compartment() == this)
243 0 : return true;
244 : } else {
245 6 : if (cx->runtime->preWrapObjectCallback) {
246 3 : obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
247 3 : if (!obj)
248 0 : return false;
249 : }
250 :
251 6 : JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
252 6 : vp->setObject(*obj);
253 : }
254 :
255 : #ifdef DEBUG
256 : {
257 97865 : JSObject *outer = obj;
258 97865 : OBJ_TO_OUTER_OBJECT(cx, outer);
259 97865 : JS_ASSERT(outer && outer == obj);
260 : }
261 : #endif
262 : }
263 :
264 : /* If we already have a wrapper for this value, use it. */
265 111225 : if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) {
266 66063 : *vp = p->value;
267 66063 : if (vp->isObject()) {
268 63335 : JSObject *obj = &vp->toObject();
269 63335 : JS_ASSERT(obj->isCrossCompartmentWrapper());
270 63335 : if (global->getClass() != &dummy_class && obj->getParent() != global) {
271 0 : do {
272 0 : if (!obj->setParent(cx, global))
273 0 : return false;
274 0 : obj = obj->getProto();
275 0 : } while (obj && obj->isCrossCompartmentWrapper());
276 : }
277 : }
278 66063 : return true;
279 : }
280 :
281 45162 : if (vp->isString()) {
282 10632 : Value orig = *vp;
283 10632 : JSString *str = vp->toString();
284 10632 : const jschar *chars = str->getChars(cx);
285 10632 : if (!chars)
286 0 : return false;
287 10632 : JSString *wrapped = js_NewStringCopyN(cx, chars, str->length());
288 10632 : if (!wrapped)
289 0 : return false;
290 10632 : vp->setString(wrapped);
291 10632 : return crossCompartmentWrappers.put(orig, *vp);
292 : }
293 :
294 34530 : JSObject *obj = &vp->toObject();
295 :
296 : /*
297 : * Recurse to wrap the prototype. Long prototype chains will run out of
298 : * stack, causing an error in CHECK_RECURSE.
299 : *
300 : * Wrapping the proto before creating the new wrapper and adding it to the
301 : * cache helps avoid leaving a bad entry in the cache on OOM. But note that
302 : * if we wrapped both proto and parent, we would get infinite recursion
303 : * here (since Object.prototype->parent->proto leads to Object.prototype
304 : * itself).
305 : */
306 34530 : JSObject *proto = obj->getProto();
307 34530 : if (!wrap(cx, &proto))
308 11 : return false;
309 :
310 : /*
311 : * We hand in the original wrapped object into the wrap hook to allow
312 : * the wrap hook to reason over what wrappers are currently applied
313 : * to the object.
314 : */
315 34519 : JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags);
316 34519 : if (!wrapper)
317 9 : return false;
318 :
319 34510 : vp->setObject(*wrapper);
320 :
321 34510 : if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false))
322 0 : return false;
323 :
324 34510 : if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp))
325 0 : return false;
326 :
327 34510 : if (!wrapper->setParent(cx, global))
328 0 : return false;
329 34510 : return true;
330 : }
331 :
332 : bool
333 1944 : JSCompartment::wrap(JSContext *cx, JSString **strp)
334 : {
335 3888 : AutoValueRooter tvr(cx, StringValue(*strp));
336 1944 : if (!wrap(cx, tvr.addr()))
337 0 : return false;
338 1944 : *strp = tvr.value().toString();
339 1944 : return true;
340 : }
341 :
342 : bool
343 36 : JSCompartment::wrap(JSContext *cx, HeapPtrString *strp)
344 : {
345 72 : AutoValueRooter tvr(cx, StringValue(*strp));
346 36 : if (!wrap(cx, tvr.addr()))
347 0 : return false;
348 36 : *strp = tvr.value().toString();
349 36 : return true;
350 : }
351 :
352 : bool
353 129061 : JSCompartment::wrap(JSContext *cx, JSObject **objp)
354 : {
355 129061 : if (!*objp)
356 5051 : return true;
357 248020 : AutoValueRooter tvr(cx, ObjectValue(**objp));
358 124010 : if (!wrap(cx, tvr.addr()))
359 11 : return false;
360 123999 : *objp = &tvr.value().toObject();
361 123999 : return true;
362 : }
363 :
364 : bool
365 585292 : JSCompartment::wrapId(JSContext *cx, jsid *idp)
366 : {
367 585292 : if (JSID_IS_INT(*idp))
368 18477 : return true;
369 1133630 : AutoValueRooter tvr(cx, IdToValue(*idp));
370 566815 : if (!wrap(cx, tvr.addr()))
371 0 : return false;
372 566815 : return ValueToId(cx, tvr.value(), idp);
373 : }
374 :
375 : bool
376 18 : JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
377 : {
378 18 : Value v = CastAsObjectJsval(*propp);
379 18 : if (!wrap(cx, &v))
380 0 : return false;
381 18 : *propp = CastAsPropertyOp(v.toObjectOrNull());
382 18 : return true;
383 : }
384 :
385 : bool
386 0 : JSCompartment::wrap(JSContext *cx, StrictPropertyOp *propp)
387 : {
388 0 : Value v = CastAsObjectJsval(*propp);
389 0 : if (!wrap(cx, &v))
390 0 : return false;
391 0 : *propp = CastAsStrictPropertyOp(v.toObjectOrNull());
392 0 : return true;
393 : }
394 :
395 : bool
396 63 : JSCompartment::wrap(JSContext *cx, PropertyDescriptor *desc)
397 : {
398 63 : return wrap(cx, &desc->obj) &&
399 81 : (!(desc->attrs & JSPROP_GETTER) || wrap(cx, &desc->getter)) &&
400 63 : (!(desc->attrs & JSPROP_SETTER) || wrap(cx, &desc->setter)) &&
401 207 : wrap(cx, &desc->value);
402 : }
403 :
404 : bool
405 22419 : JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
406 : {
407 22419 : jsid *vector = props.begin();
408 22419 : int length = props.length();
409 45918 : for (size_t n = 0; n < size_t(length); ++n) {
410 23499 : if (!wrapId(cx, &vector[n]))
411 0 : return false;
412 : }
413 22419 : return true;
414 : }
415 :
416 : /*
417 : * This method marks pointers that cross compartment boundaries. It should be
418 : * called only for per-compartment GCs, since full GCs naturally follow pointers
419 : * across compartments.
420 : */
421 : void
422 324 : JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
423 : {
424 324 : JS_ASSERT(!isCollecting());
425 :
426 630 : for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
427 306 : Value tmp = e.front().key;
428 306 : MarkValueRoot(trc, &tmp, "cross-compartment wrapper");
429 306 : JS_ASSERT(tmp == e.front().key);
430 : }
431 324 : }
432 :
433 : void
434 20 : JSCompartment::markTypes(JSTracer *trc)
435 : {
436 : /*
437 : * Mark all scripts, type objects and singleton JS objects in the
438 : * compartment. These can be referred to directly by type sets, which we
439 : * cannot modify while code which depends on these type sets is active.
440 : */
441 20 : JS_ASSERT(activeAnalysis);
442 :
443 241 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
444 221 : JSScript *script = i.get<JSScript>();
445 221 : MarkScriptRoot(trc, &script, "mark_types_script");
446 221 : JS_ASSERT(script == i.get<JSScript>());
447 : }
448 :
449 260 : for (size_t thingKind = FINALIZE_OBJECT0;
450 : thingKind < FINALIZE_OBJECT_LIMIT;
451 : thingKind++) {
452 8831 : for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) {
453 8591 : JSObject *object = i.get<JSObject>();
454 8591 : if (object->hasSingletonType()) {
455 6742 : MarkObjectRoot(trc, &object, "mark_types_singleton");
456 6742 : JS_ASSERT(object == i.get<JSObject>());
457 : }
458 : }
459 : }
460 :
461 380 : for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
462 360 : types::TypeObject *type = i.get<types::TypeObject>();
463 360 : MarkTypeObjectRoot(trc, &type, "mark_types_scan");
464 360 : JS_ASSERT(type == i.get<types::TypeObject>());
465 : }
466 20 : }
467 :
468 : void
469 94341 : JSCompartment::discardJitCode(FreeOp *fop)
470 : {
471 : /*
472 : * Kick all frames on the stack into the interpreter, and release all JIT
473 : * code in the compartment.
474 : */
475 : #ifdef JS_METHODJIT
476 94341 : mjit::ClearAllFrames(this);
477 :
478 549656 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
479 455315 : JSScript *script = i.get<JSScript>();
480 455315 : mjit::ReleaseScriptCode(fop, script);
481 :
482 : /*
483 : * Use counts for scripts are reset on GC. After discarding code we
484 : * need to let it warm back up to get information like which opcodes
485 : * are setting array holes or accessing getter properties.
486 : */
487 455315 : script->resetUseCount();
488 : }
489 : #endif
490 94341 : }
491 :
492 : void
493 83697 : JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
494 : {
495 : /* Remove dead wrappers from the table. */
496 139657 : for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
497 99909 : JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key) &&
498 : !IsAboutToBeFinalized(e.front().value),
499 99909 : e.front().key.isString());
500 67972 : if (IsAboutToBeFinalized(e.front().key) ||
501 12012 : IsAboutToBeFinalized(e.front().value)) {
502 45139 : e.removeFront();
503 : }
504 : }
505 :
506 : /* Remove dead references held weakly by the compartment. */
507 :
508 83697 : regExps.sweep(rt);
509 :
510 83697 : sweepBaseShapeTable();
511 83697 : sweepInitialShapeTable();
512 83697 : sweepNewTypeObjectTable(newTypeObjects);
513 83697 : sweepNewTypeObjectTable(lazyTypeObjects);
514 :
515 83697 : if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject))
516 22524 : emptyTypeObject = NULL;
517 :
518 83697 : newObjectCache.reset();
519 :
520 83697 : sweepBreakpoints(fop);
521 :
522 : {
523 167394 : gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
524 83697 : discardJitCode(fop);
525 : }
526 :
527 83697 : if (!activeAnalysis) {
528 167354 : gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
529 :
530 : /*
531 : * Clear the analysis pool, but don't release its data yet. While
532 : * sweeping types any live data will be allocated into the pool.
533 : */
534 167354 : LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
535 83677 : oldAlloc.steal(&typeLifoAlloc);
536 :
537 : /*
538 : * Periodically release observed types for all scripts. This is safe to
539 : * do when there are no frames for the compartment on the stack.
540 : */
541 83677 : if (active)
542 21606 : releaseTypes = false;
543 :
544 : /*
545 : * Sweep analysis information and everything depending on it from the
546 : * compartment, including all remaining mjit code if inference is
547 : * enabled in the compartment.
548 : */
549 83677 : if (types.inferenceEnabled) {
550 49284 : gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
551 :
552 249934 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
553 225292 : JSScript *script = i.get<JSScript>();
554 225292 : if (script->types) {
555 117460 : types::TypeScript::Sweep(fop, script);
556 :
557 117460 : if (releaseTypes) {
558 70 : script->types->destroy();
559 70 : script->types = NULL;
560 70 : script->typesPurged = true;
561 : }
562 : }
563 : }
564 : }
565 :
566 : {
567 167354 : gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
568 83677 : types.sweep(fop);
569 : }
570 :
571 : {
572 167354 : gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
573 495754 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
574 412077 : JSScript *script = i.get<JSScript>();
575 412077 : script->clearAnalysis();
576 : }
577 : }
578 : }
579 :
580 83697 : active = false;
581 83697 : }
582 :
583 : void
584 89042 : JSCompartment::purge()
585 : {
586 89042 : dtoaCache.purge();
587 :
588 : /*
589 : * Clear the hash and reset all evalHashLink to null before the GC. This
590 : * way MarkChildren(trc, JSScript *) can assume that JSScript::u.object is
591 : * not null when we have script owned by an object and not from the eval
592 : * cache.
593 : */
594 5787730 : for (size_t i = 0; i < ArrayLength(evalCache); ++i) {
595 11431367 : for (JSScript **listHeadp = &evalCache[i]; *listHeadp; ) {
596 33991 : JSScript *script = *listHeadp;
597 33991 : JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
598 33991 : *listHeadp = NULL;
599 33991 : listHeadp = &script->evalHashLink();
600 : }
601 : }
602 :
603 89042 : nativeIterCache.purge();
604 89042 : toSourceCache.destroyIfConstructed();
605 89042 : }
606 :
607 : void
608 125423 : JSCompartment::resetGCMallocBytes()
609 : {
610 125423 : gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
611 125423 : }
612 :
613 : void
614 41285 : JSCompartment::setGCMaxMallocBytes(size_t value)
615 : {
616 : /*
617 : * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
618 : * mean that value.
619 : */
620 41285 : gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
621 41285 : resetGCMallocBytes();
622 41285 : }
623 :
624 : void
625 108 : JSCompartment::onTooMuchMalloc()
626 : {
627 108 : TriggerCompartmentGC(this, gcreason::TOO_MUCH_MALLOC);
628 108 : }
629 :
630 :
631 : MathCache *
632 183 : JSCompartment::allocMathCache(JSContext *cx)
633 : {
634 183 : JS_ASSERT(!mathCache);
635 183 : mathCache = cx->new_<MathCache>();
636 183 : if (!mathCache)
637 0 : js_ReportOutOfMemory(cx);
638 183 : return mathCache;
639 : }
640 :
641 : bool
642 19525 : JSCompartment::hasScriptsOnStack()
643 : {
644 26842 : for (AllFramesIter i(rt->stackSpace); !i.done(); ++i) {
645 7329 : JSScript *script = i.fp()->maybeScript();
646 7329 : if (script && script->compartment() == this)
647 12 : return true;
648 : }
649 19513 : return false;
650 : }
651 :
652 : bool
653 6940 : JSCompartment::setDebugModeFromC(JSContext *cx, bool b)
654 : {
655 6940 : bool enabledBefore = debugMode();
656 6940 : bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b;
657 :
658 : // Debug mode can be enabled only when no scripts from the target
659 : // compartment are on the stack. It would even be incorrect to discard just
660 : // the non-live scripts' JITScripts because they might share ICs with live
661 : // scripts (bug 632343).
662 : //
663 : // We do allow disabling debug mode while scripts are on the stack. In
664 : // that case the debug-mode code for those scripts remains, so subsequently
665 : // hooks may be called erroneously, even though debug mode is supposedly
666 : // off, and we have to live with it.
667 : //
668 6940 : bool onStack = false;
669 6940 : if (enabledBefore != enabledAfter) {
670 6660 : onStack = hasScriptsOnStack();
671 6660 : if (b && onStack) {
672 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
673 0 : return false;
674 : }
675 : }
676 :
677 6940 : debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0);
678 6940 : JS_ASSERT(debugMode() == enabledAfter);
679 6940 : if (enabledBefore != enabledAfter)
680 6660 : updateForDebugMode(cx->runtime->defaultFreeOp());
681 6940 : return true;
682 : }
683 :
684 : void
685 10797 : JSCompartment::updateForDebugMode(FreeOp *fop)
686 : {
687 19682 : for (ContextIter acx(rt); !acx.done(); acx.next()) {
688 8885 : if (acx->compartment == this)
689 8728 : acx->updateJITEnabled();
690 : }
691 :
692 : #ifdef JS_METHODJIT
693 10797 : bool enabled = debugMode();
694 :
695 10797 : if (enabled)
696 8728 : JS_ASSERT(!hasScriptsOnStack());
697 2069 : else if (hasScriptsOnStack())
698 12 : return;
699 :
700 : /*
701 : * Discard JIT code and bytecode analyses for any scripts that change
702 : * debugMode.
703 : */
704 32717 : for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
705 21932 : JSScript *script = i.get<JSScript>();
706 21932 : if (script->debugMode != enabled) {
707 16345 : mjit::ReleaseScriptCode(fop, script);
708 16345 : script->clearAnalysis();
709 16345 : script->debugMode = enabled;
710 : }
711 : }
712 : #endif
713 : }
714 :
715 : bool
716 3353 : JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global)
717 : {
718 3353 : bool wasEnabled = debugMode();
719 3353 : if (!debuggees.put(global)) {
720 0 : js_ReportOutOfMemory(cx);
721 0 : return false;
722 : }
723 3353 : debugModeBits |= DebugFromJS;
724 3353 : if (!wasEnabled)
725 2068 : updateForDebugMode(cx->runtime->defaultFreeOp());
726 3353 : return true;
727 : }
728 :
729 : void
730 3353 : JSCompartment::removeDebuggee(FreeOp *fop,
731 : js::GlobalObject *global,
732 : js::GlobalObjectSet::Enum *debuggeesEnum)
733 : {
734 3353 : bool wasEnabled = debugMode();
735 3353 : JS_ASSERT(debuggees.has(global));
736 3353 : if (debuggeesEnum)
737 90 : debuggeesEnum->removeFront();
738 : else
739 3263 : debuggees.remove(global);
740 :
741 3353 : if (debuggees.empty()) {
742 3326 : debugModeBits &= ~DebugFromJS;
743 3326 : if (wasEnabled && !debugMode())
744 2069 : updateForDebugMode(fop);
745 : }
746 3353 : }
747 :
748 : void
749 36 : JSCompartment::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler)
750 : {
751 108 : for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
752 72 : JSScript *script = i.get<JSScript>();
753 72 : if (script->hasAnyBreakpointsOrStepMode())
754 36 : script->clearBreakpointsIn(cx, dbg, handler);
755 : }
756 36 : }
757 :
758 : void
759 41261 : JSCompartment::clearTraps(JSContext *cx)
760 : {
761 316723 : for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
762 275462 : JSScript *script = i.get<JSScript>();
763 275462 : if (script->hasAnyBreakpointsOrStepMode())
764 515 : script->clearTraps(cx->runtime->defaultFreeOp());
765 : }
766 41261 : }
767 :
768 : void
769 83697 : JSCompartment::sweepBreakpoints(FreeOp *fop)
770 : {
771 83697 : if (JS_CLIST_IS_EMPTY(&rt->debuggerList))
772 71619 : return;
773 :
774 74531 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
775 62453 : JSScript *script = i.get<JSScript>();
776 62453 : if (!script->hasAnyBreakpointsOrStepMode())
777 62228 : continue;
778 225 : bool scriptGone = IsAboutToBeFinalized(script);
779 7398 : for (unsigned i = 0; i < script->length; i++) {
780 7173 : BreakpointSite *site = script->getBreakpointSite(script->code + i);
781 7173 : if (!site)
782 6849 : continue;
783 : // nextbp is necessary here to avoid possibly reading *bp after
784 : // destroying it.
785 : Breakpoint *nextbp;
786 945 : for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
787 621 : nextbp = bp->nextInSite();
788 621 : if (scriptGone || IsAboutToBeFinalized(bp->debugger->toJSObject()))
789 513 : bp->destroy(fop);
790 : }
791 : }
792 : }
793 : }
794 :
795 : size_t
796 0 : JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf)
797 : {
798 0 : return baseShapes.sizeOfExcludingThis(mallocSizeOf)
799 0 : + initialShapes.sizeOfExcludingThis(mallocSizeOf)
800 0 : + newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
801 0 : + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
802 : }
|