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 45576 : JSCompartment::JSCompartment(JSRuntime *rt)
73 : : rt(rt),
74 : principals(NULL),
75 : needsBarrier_(false),
76 : gcBytes(0),
77 : gcTriggerBytes(0),
78 : hold(false),
79 : typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
80 : data(NULL),
81 : active(false),
82 : #ifdef JS_METHODJIT
83 : jaegerCompartment_(NULL),
84 : #endif
85 : regExps(rt),
86 : propertyTree(thisForCtor()),
87 : emptyTypeObject(NULL),
88 : gcMallocAndFreeBytes(0),
89 : gcTriggerMallocAndFreeBytes(0),
90 : gcMallocBytes(0),
91 : debugModeBits(rt->debugMode ? DebugFromC : 0),
92 : mathCache(NULL),
93 45576 : watchpointMap(NULL)
94 : {
95 45576 : PodArrayZero(evalCache);
96 45576 : setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9);
97 45576 : }
98 :
99 91142 : JSCompartment::~JSCompartment()
100 : {
101 : #ifdef JS_METHODJIT
102 45571 : Foreground::delete_(jaegerCompartment_);
103 : #endif
104 :
105 45571 : Foreground::delete_(mathCache);
106 45571 : Foreground::delete_(watchpointMap);
107 :
108 : #ifdef DEBUG
109 2962115 : for (size_t i = 0; i < ArrayLength(evalCache); ++i)
110 2916544 : JS_ASSERT(!evalCache[i]);
111 : #endif
112 45571 : }
113 :
114 : bool
115 45576 : JSCompartment::init(JSContext *cx)
116 : {
117 45576 : activeAnalysis = activeInference = false;
118 45576 : types.init(cx);
119 :
120 45576 : newObjectCache.reset();
121 :
122 45576 : if (!crossCompartmentWrappers.init())
123 0 : return false;
124 :
125 45576 : if (!regExps.init(cx))
126 0 : return false;
127 :
128 45576 : if (!scriptFilenameTable.init())
129 0 : return false;
130 :
131 45576 : return debuggees.init();
132 : }
133 :
134 : #ifdef JS_METHODJIT
135 : bool
136 16184670 : JSCompartment::ensureJaegerCompartmentExists(JSContext *cx)
137 : {
138 16184670 : if (jaegerCompartment_)
139 16172844 : return true;
140 :
141 11826 : mjit::JaegerCompartment *jc = cx->new_<mjit::JaegerCompartment>();
142 11826 : if (!jc)
143 0 : return false;
144 11826 : if (!jc->Initialize(cx)) {
145 0 : cx->delete_(jc);
146 0 : return false;
147 : }
148 11826 : jaegerCompartment_ = jc;
149 11826 : return true;
150 : }
151 :
152 : size_t
153 18 : JSCompartment::sizeOfMjitCode() const
154 : {
155 18 : if (!jaegerCompartment_)
156 12 : return 0;
157 :
158 : size_t method, regexp, unused;
159 6 : jaegerCompartment_->execAlloc()->sizeOfCode(&method, ®exp, &unused);
160 6 : JS_ASSERT(regexp == 0);
161 6 : return method + unused;
162 : }
163 :
164 : #endif
165 :
166 : bool
167 2625178 : JSCompartment::wrap(JSContext *cx, Value *vp)
168 : {
169 2625178 : JS_ASSERT(cx->compartment == this);
170 :
171 2625178 : unsigned flags = 0;
172 :
173 2625178 : JS_CHECK_RECURSION(cx, return false);
174 :
175 : /* Only GC things have to be wrapped or copied. */
176 2625170 : if (!vp->isMarkable())
177 77386 : return true;
178 :
179 2547784 : if (vp->isString()) {
180 555980 : JSString *str = vp->toString();
181 :
182 : /* If the string is already in this compartment, we are done. */
183 555980 : if (str->compartment() == this)
184 460 : return true;
185 :
186 : /* If the string is an atom, we don't have to copy. */
187 555520 : if (str->isAtom()) {
188 541315 : JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment);
189 541315 : return true;
190 : }
191 : }
192 :
193 : /*
194 : * Wrappers should really be parented to the wrapped parent of the wrapped
195 : * object, but in that case a wrapped global object would have a NULL
196 : * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
197 : * we parent all wrappers to the global object in their home compartment.
198 : * This loses us some transparency, and is generally very cheesy.
199 : */
200 : JSObject *global;
201 2006009 : if (cx->hasfp()) {
202 1841496 : global = &cx->fp()->scopeChain().global();
203 : } else {
204 164513 : global = JS_ObjectToInnerObject(cx, cx->globalObject);
205 164513 : if (!global)
206 0 : return false;
207 : }
208 :
209 : /* Unwrap incoming objects. */
210 2006009 : if (vp->isObject()) {
211 1991804 : JSObject *obj = &vp->toObject();
212 :
213 : /* If the object is already in this compartment, we are done. */
214 1991804 : if (obj->compartment() == this)
215 1834480 : return true;
216 :
217 : /* Translate StopIteration singleton. */
218 157324 : if (obj->isStopIteration())
219 0 : return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp);
220 :
221 : /* Don't unwrap an outer window proxy. */
222 157324 : if (!obj->getClass()->ext.innerObject) {
223 157318 : obj = UnwrapObject(&vp->toObject(), true, &flags);
224 157318 : vp->setObject(*obj);
225 157318 : if (obj->compartment() == this)
226 71460 : return true;
227 :
228 85858 : if (cx->runtime->preWrapObjectCallback) {
229 19477 : obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
230 19477 : if (!obj)
231 0 : return false;
232 : }
233 :
234 85858 : vp->setObject(*obj);
235 85858 : if (obj->compartment() == this)
236 321 : return true;
237 : } else {
238 6 : if (cx->runtime->preWrapObjectCallback) {
239 3 : obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
240 3 : if (!obj)
241 0 : return false;
242 : }
243 :
244 6 : JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
245 6 : vp->setObject(*obj);
246 : }
247 :
248 : #ifdef DEBUG
249 : {
250 85543 : JSObject *outer = obj;
251 85543 : OBJ_TO_OUTER_OBJECT(cx, outer);
252 85543 : JS_ASSERT(outer && outer == obj);
253 : }
254 : #endif
255 : }
256 :
257 : /* If we already have a wrapper for this value, use it. */
258 99748 : if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) {
259 47119 : *vp = p->value;
260 47119 : if (vp->isObject()) {
261 44399 : JSObject *obj = &vp->toObject();
262 44399 : JS_ASSERT(obj->isCrossCompartmentWrapper());
263 44399 : if (global->getClass() != &dummy_class && obj->getParent() != global) {
264 5 : do {
265 5 : if (!obj->setParent(cx, global))
266 0 : return false;
267 5 : obj = obj->getProto();
268 0 : } while (obj && obj->isCrossCompartmentWrapper());
269 : }
270 : }
271 47119 : return true;
272 : }
273 :
274 52629 : if (vp->isString()) {
275 11485 : Value orig = *vp;
276 11485 : JSString *str = vp->toString();
277 11485 : const jschar *chars = str->getChars(cx);
278 11485 : if (!chars)
279 0 : return false;
280 11485 : JSString *wrapped = js_NewStringCopyN(cx, chars, str->length());
281 11485 : if (!wrapped)
282 0 : return false;
283 11485 : vp->setString(wrapped);
284 11485 : return crossCompartmentWrappers.put(orig, *vp);
285 : }
286 :
287 41144 : JSObject *obj = &vp->toObject();
288 :
289 : /*
290 : * Recurse to wrap the prototype. Long prototype chains will run out of
291 : * stack, causing an error in CHECK_RECURSE.
292 : *
293 : * Wrapping the proto before creating the new wrapper and adding it to the
294 : * cache helps avoid leaving a bad entry in the cache on OOM. But note that
295 : * if we wrapped both proto and parent, we would get infinite recursion
296 : * here (since Object.prototype->parent->proto leads to Object.prototype
297 : * itself).
298 : */
299 41144 : JSObject *proto = obj->getProto();
300 41144 : if (!wrap(cx, &proto))
301 14 : return false;
302 :
303 : /*
304 : * We hand in the original wrapped object into the wrap hook to allow
305 : * the wrap hook to reason over what wrappers are currently applied
306 : * to the object.
307 : */
308 41130 : JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags);
309 41130 : if (!wrapper)
310 9 : return false;
311 :
312 41121 : vp->setObject(*wrapper);
313 :
314 41121 : if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false))
315 0 : return false;
316 :
317 41121 : if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp))
318 0 : return false;
319 :
320 41121 : if (!wrapper->setParent(cx, global))
321 0 : return false;
322 41121 : return true;
323 : }
324 :
325 : bool
326 1970 : JSCompartment::wrap(JSContext *cx, JSString **strp)
327 : {
328 3940 : AutoValueRooter tvr(cx, StringValue(*strp));
329 1970 : if (!wrap(cx, tvr.addr()))
330 0 : return false;
331 1970 : *strp = tvr.value().toString();
332 1970 : return true;
333 : }
334 :
335 : bool
336 36 : JSCompartment::wrap(JSContext *cx, HeapPtrString *strp)
337 : {
338 72 : AutoValueRooter tvr(cx, StringValue(*strp));
339 36 : if (!wrap(cx, tvr.addr()))
340 0 : return false;
341 36 : *strp = tvr.value().toString();
342 36 : return true;
343 : }
344 :
345 : bool
346 1614547 : JSCompartment::wrap(JSContext *cx, JSObject **objp)
347 : {
348 1614547 : if (!*objp)
349 6210 : return true;
350 3216674 : AutoValueRooter tvr(cx, ObjectValue(**objp));
351 1608337 : if (!wrap(cx, tvr.addr()))
352 14 : return false;
353 1608323 : *objp = &tvr.value().toObject();
354 1608323 : return true;
355 : }
356 :
357 : bool
358 430446 : JSCompartment::wrapId(JSContext *cx, jsid *idp)
359 : {
360 430446 : if (JSID_IS_INT(*idp))
361 838 : return true;
362 859216 : AutoValueRooter tvr(cx, IdToValue(*idp));
363 429608 : if (!wrap(cx, tvr.addr()))
364 0 : return false;
365 429608 : return ValueToId(cx, tvr.value(), idp);
366 : }
367 :
368 : bool
369 18 : JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
370 : {
371 18 : Value v = CastAsObjectJsval(*propp);
372 18 : if (!wrap(cx, &v))
373 0 : return false;
374 18 : *propp = CastAsPropertyOp(v.toObjectOrNull());
375 18 : return true;
376 : }
377 :
378 : bool
379 0 : JSCompartment::wrap(JSContext *cx, StrictPropertyOp *propp)
380 : {
381 0 : Value v = CastAsObjectJsval(*propp);
382 0 : if (!wrap(cx, &v))
383 0 : return false;
384 0 : *propp = CastAsStrictPropertyOp(v.toObjectOrNull());
385 0 : return true;
386 : }
387 :
388 : bool
389 63 : JSCompartment::wrap(JSContext *cx, PropertyDescriptor *desc)
390 : {
391 63 : return wrap(cx, &desc->obj) &&
392 81 : (!(desc->attrs & JSPROP_GETTER) || wrap(cx, &desc->getter)) &&
393 63 : (!(desc->attrs & JSPROP_SETTER) || wrap(cx, &desc->setter)) &&
394 207 : wrap(cx, &desc->value);
395 : }
396 :
397 : bool
398 9 : JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
399 : {
400 9 : jsid *vector = props.begin();
401 9 : int length = props.length();
402 18 : for (size_t n = 0; n < size_t(length); ++n) {
403 9 : if (!wrapId(cx, &vector[n]))
404 0 : return false;
405 : }
406 9 : return true;
407 : }
408 :
409 : /*
410 : * This method marks pointers that cross compartment boundaries. It should be
411 : * called only for per-compartment GCs, since full GCs naturally follow pointers
412 : * across compartments.
413 : */
414 : void
415 540 : JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
416 : {
417 540 : JS_ASSERT(trc->runtime->gcCurrentCompartment);
418 :
419 927 : for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
420 387 : Value tmp = e.front().key;
421 387 : MarkValueRoot(trc, &tmp, "cross-compartment wrapper");
422 387 : JS_ASSERT(tmp == e.front().key);
423 : }
424 540 : }
425 :
426 : void
427 36 : JSCompartment::markTypes(JSTracer *trc)
428 : {
429 : /*
430 : * Mark all scripts, type objects and singleton JS objects in the
431 : * compartment. These can be referred to directly by type sets, which we
432 : * cannot modify while code which depends on these type sets is active.
433 : */
434 36 : JS_ASSERT(activeAnalysis);
435 :
436 295 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
437 259 : JSScript *script = i.get<JSScript>();
438 259 : MarkScriptRoot(trc, &script, "mark_types_script");
439 259 : JS_ASSERT(script == i.get<JSScript>());
440 : }
441 :
442 468 : for (size_t thingKind = FINALIZE_OBJECT0;
443 : thingKind < FINALIZE_OBJECT_LIMIT;
444 : thingKind++) {
445 14795 : for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) {
446 14363 : JSObject *object = i.get<JSObject>();
447 14363 : if (object->hasSingletonType()) {
448 11256 : MarkObjectRoot(trc, &object, "mark_types_singleton");
449 11256 : JS_ASSERT(object == i.get<JSObject>());
450 : }
451 : }
452 : }
453 :
454 590 : for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
455 554 : types::TypeObject *type = i.get<types::TypeObject>();
456 554 : MarkTypeObjectRoot(trc, &type, "mark_types_scan");
457 554 : JS_ASSERT(type == i.get<types::TypeObject>());
458 : }
459 36 : }
460 :
461 : void
462 133015 : JSCompartment::discardJitCode(JSContext *cx)
463 : {
464 : /*
465 : * Kick all frames on the stack into the interpreter, and release all JIT
466 : * code in the compartment.
467 : */
468 : #ifdef JS_METHODJIT
469 133015 : mjit::ClearAllFrames(this);
470 :
471 8241549 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
472 8108534 : JSScript *script = i.get<JSScript>();
473 8108534 : mjit::ReleaseScriptCode(cx, script);
474 :
475 : /*
476 : * Use counts for scripts are reset on GC. After discarding code we
477 : * need to let it warm back up to get information like which opcodes
478 : * are setting array holes or accessing getter properties.
479 : */
480 8108534 : script->resetUseCount();
481 : }
482 : #endif
483 133015 : }
484 :
485 : void
486 122478 : JSCompartment::sweep(JSContext *cx, bool releaseTypes)
487 : {
488 : /* Remove dead wrappers from the table. */
489 180527 : for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
490 105391 : JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key) &&
491 : !IsAboutToBeFinalized(e.front().value),
492 105391 : e.front().key.isString());
493 68781 : if (IsAboutToBeFinalized(e.front().key) ||
494 10732 : IsAboutToBeFinalized(e.front().value)) {
495 52603 : e.removeFront();
496 : }
497 : }
498 :
499 : /* Remove dead references held weakly by the compartment. */
500 :
501 122478 : regExps.sweep(rt);
502 :
503 122478 : sweepBaseShapeTable(cx);
504 122478 : sweepInitialShapeTable(cx);
505 122478 : sweepNewTypeObjectTable(cx, newTypeObjects);
506 122478 : sweepNewTypeObjectTable(cx, lazyTypeObjects);
507 :
508 122478 : if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject))
509 25653 : emptyTypeObject = NULL;
510 :
511 122478 : newObjectCache.reset();
512 :
513 122478 : sweepBreakpoints(cx);
514 :
515 : {
516 244956 : gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
517 122478 : discardJitCode(cx);
518 : }
519 :
520 122478 : if (!activeAnalysis) {
521 244884 : gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
522 :
523 : /*
524 : * Clear the analysis pool, but don't release its data yet. While
525 : * sweeping types any live data will be allocated into the pool.
526 : */
527 244884 : LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
528 122442 : oldAlloc.steal(&typeLifoAlloc);
529 :
530 : /*
531 : * Periodically release observed types for all scripts. This is safe to
532 : * do when there are no frames for the compartment on the stack.
533 : */
534 122442 : if (active)
535 21178 : releaseTypes = false;
536 :
537 : /*
538 : * Sweep analysis information and everything depending on it from the
539 : * compartment, including all remaining mjit code if inference is
540 : * enabled in the compartment.
541 : */
542 122442 : if (types.inferenceEnabled) {
543 220611 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
544 197053 : JSScript *script = i.get<JSScript>();
545 197053 : if (script->types) {
546 114034 : types::TypeScript::Sweep(cx, script);
547 :
548 114034 : if (releaseTypes) {
549 70 : script->types->destroy();
550 70 : script->types = NULL;
551 70 : script->typesPurged = true;
552 : }
553 : }
554 : }
555 : }
556 :
557 122442 : types.sweep(cx);
558 :
559 8192641 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
560 8070199 : JSScript *script = i.get<JSScript>();
561 8070199 : script->clearAnalysis();
562 : }
563 : }
564 :
565 122478 : active = false;
566 122478 : }
567 :
568 : void
569 127756 : JSCompartment::purge()
570 : {
571 127756 : dtoaCache.purge();
572 :
573 : /*
574 : * Clear the hash and reset all evalHashLink to null before the GC. This
575 : * way MarkChildren(trc, JSScript *) can assume that JSScript::u.object is
576 : * not null when we have script owned by an object and not from the eval
577 : * cache.
578 : */
579 8304140 : for (size_t i = 0; i < ArrayLength(evalCache); ++i) {
580 16386856 : for (JSScript **listHeadp = &evalCache[i]; *listHeadp; ) {
581 34088 : JSScript *script = *listHeadp;
582 34088 : JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
583 34088 : *listHeadp = NULL;
584 34088 : listHeadp = &script->evalHashLink();
585 : }
586 : }
587 :
588 127756 : nativeIterCache.purge();
589 127756 : toSourceCache.destroyIfConstructed();
590 127756 : }
591 :
592 : void
593 168378 : JSCompartment::resetGCMallocBytes()
594 : {
595 168378 : gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
596 168378 : }
597 :
598 : void
599 45576 : JSCompartment::setGCMaxMallocBytes(size_t value)
600 : {
601 : /*
602 : * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
603 : * mean that value.
604 : */
605 45576 : gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
606 45576 : resetGCMallocBytes();
607 45576 : }
608 :
609 : void
610 118 : JSCompartment::onTooMuchMalloc()
611 : {
612 118 : TriggerCompartmentGC(this, gcreason::TOO_MUCH_MALLOC);
613 118 : }
614 :
615 :
616 : MathCache *
617 174 : JSCompartment::allocMathCache(JSContext *cx)
618 : {
619 174 : JS_ASSERT(!mathCache);
620 174 : mathCache = cx->new_<MathCache>();
621 174 : if (!mathCache)
622 0 : js_ReportOutOfMemory(cx);
623 174 : return mathCache;
624 : }
625 :
626 : bool
627 20372 : JSCompartment::hasScriptsOnStack()
628 : {
629 28106 : for (AllFramesIter i(rt->stackSpace); !i.done(); ++i) {
630 7746 : JSScript *script = i.fp()->maybeScript();
631 7746 : if (script && script->compartment() == this)
632 12 : return true;
633 : }
634 20360 : return false;
635 : }
636 :
637 : bool
638 7413 : JSCompartment::setDebugModeFromC(JSContext *cx, bool b)
639 : {
640 7413 : bool enabledBefore = debugMode();
641 7413 : bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b;
642 :
643 : // Debug mode can be enabled only when no scripts from the target
644 : // compartment are on the stack. It would even be incorrect to discard just
645 : // the non-live scripts' JITScripts because they might share ICs with live
646 : // scripts (bug 632343).
647 : //
648 : // We do allow disabling debug mode while scripts are on the stack. In
649 : // that case the debug-mode code for those scripts remains, so subsequently
650 : // hooks may be called erroneously, even though debug mode is supposedly
651 : // off, and we have to live with it.
652 : //
653 7413 : bool onStack = false;
654 7413 : if (enabledBefore != enabledAfter) {
655 7133 : onStack = hasScriptsOnStack();
656 7133 : if (b && onStack) {
657 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
658 0 : return false;
659 : }
660 : }
661 :
662 7413 : debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0);
663 7413 : JS_ASSERT(debugMode() == enabledAfter);
664 7413 : if (enabledBefore != enabledAfter)
665 7133 : updateForDebugMode(cx);
666 7413 : return true;
667 : }
668 :
669 : void
670 11204 : JSCompartment::updateForDebugMode(JSContext *cx)
671 : {
672 21874 : for (ContextIter acx(rt); !acx.done(); acx.next()) {
673 10670 : if (acx->compartment == this)
674 9168 : acx->updateJITEnabled();
675 : }
676 :
677 : #ifdef JS_METHODJIT
678 11204 : bool enabled = debugMode();
679 :
680 11204 : if (enabled)
681 9168 : JS_ASSERT(!hasScriptsOnStack());
682 2036 : else if (hasScriptsOnStack())
683 12 : return;
684 :
685 : /*
686 : * Discard JIT code and bytecode analyses for any scripts that change
687 : * debugMode.
688 : */
689 51793 : for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
690 40601 : JSScript *script = i.get<JSScript>();
691 40601 : if (script->debugMode != enabled) {
692 34982 : mjit::ReleaseScriptCode(cx, script);
693 34982 : script->clearAnalysis();
694 34982 : script->debugMode = enabled;
695 : }
696 : }
697 : #endif
698 : }
699 :
700 : bool
701 3281 : JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global)
702 : {
703 3281 : bool wasEnabled = debugMode();
704 3281 : if (!debuggees.put(global)) {
705 0 : js_ReportOutOfMemory(cx);
706 0 : return false;
707 : }
708 3281 : debugModeBits |= DebugFromJS;
709 3281 : if (!wasEnabled)
710 2035 : updateForDebugMode(cx);
711 3281 : return true;
712 : }
713 :
714 : void
715 3281 : JSCompartment::removeDebuggee(JSContext *cx,
716 : js::GlobalObject *global,
717 : js::GlobalObjectSet::Enum *debuggeesEnum)
718 : {
719 3281 : bool wasEnabled = debugMode();
720 3281 : JS_ASSERT(debuggees.has(global));
721 3281 : if (debuggeesEnum)
722 89 : debuggeesEnum->removeFront();
723 : else
724 3192 : debuggees.remove(global);
725 :
726 3281 : if (debuggees.empty()) {
727 3254 : debugModeBits &= ~DebugFromJS;
728 3254 : if (wasEnabled && !debugMode())
729 2036 : updateForDebugMode(cx);
730 : }
731 3281 : }
732 :
733 : void
734 36 : JSCompartment::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler)
735 : {
736 108 : for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
737 72 : JSScript *script = i.get<JSScript>();
738 72 : if (script->hasAnyBreakpointsOrStepMode())
739 36 : script->clearBreakpointsIn(cx, dbg, handler);
740 : }
741 36 : }
742 :
743 : void
744 41941 : JSCompartment::clearTraps(JSContext *cx)
745 : {
746 279734 : for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
747 237793 : JSScript *script = i.get<JSScript>();
748 237793 : if (script->hasAnyBreakpointsOrStepMode())
749 515 : script->clearTraps(cx);
750 : }
751 41941 : }
752 :
753 : void
754 122478 : JSCompartment::sweepBreakpoints(JSContext *cx)
755 : {
756 122478 : if (JS_CLIST_IS_EMPTY(&cx->runtime->debuggerList))
757 110515 : return;
758 :
759 83734 : for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
760 71771 : JSScript *script = i.get<JSScript>();
761 71771 : if (!script->hasAnyBreakpointsOrStepMode())
762 71546 : continue;
763 225 : bool scriptGone = IsAboutToBeFinalized(script);
764 7389 : for (unsigned i = 0; i < script->length; i++) {
765 7164 : BreakpointSite *site = script->getBreakpointSite(script->code + i);
766 7164 : if (!site)
767 6840 : continue;
768 : // nextbp is necessary here to avoid possibly reading *bp after
769 : // destroying it.
770 : Breakpoint *nextbp;
771 945 : for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
772 621 : nextbp = bp->nextInSite();
773 621 : if (scriptGone || IsAboutToBeFinalized(bp->debugger->toJSObject()))
774 513 : bp->destroy(cx);
775 : }
776 : }
777 : }
778 : }
779 :
780 : size_t
781 9 : JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf)
782 : {
783 9 : return baseShapes.sizeOfExcludingThis(mallocSizeOf)
784 9 : + initialShapes.sizeOfExcludingThis(mallocSizeOf)
785 9 : + newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
786 18 : + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
787 : }
|