1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
22 * Portions created by the Initial Developer are Copyright (C) 2010
23 * the Initial Developer. All Rights Reserved.
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.
39 * ***** END LICENSE BLOCK ***** */
42 #include "jscompartment.h"
48 #include "jswrapper.h"
49 #include "assembler/wtf/Platform.h"
50 #include "methodjit/MethodJIT.h"
51 #include "methodjit/PolyIC.h"
52 #include "methodjit/MonoIC.h"
54 #include "jsgcinlines.h"
57 #include "assembler/jit/ExecutableAllocator.h"
61 using namespace js::gc;
63 JSCompartment::JSCompartment(JSRuntime *rt)
72 jaegerCompartment(NULL),
74 propertyTree(thisForCtor()),
75 debugMode(rt->debugMode),
77 regExpAllocator(NULL),
82 JS_INIT_CLIST(&scripts);
85 /* InitJIT expects this area to be zero'd. */
86 PodZero(&traceMonitor);
89 PodArrayZero(scriptsToGC);
92 JSCompartment::~JSCompartment()
94 Shape::finishEmptyShapes(this);
95 propertyTree.finish();
98 js_delete(regExpAllocator);
101 #if defined JS_TRACER
102 FinishJIT(&traceMonitor);
106 js_delete(jaegerCompartment);
109 js_delete(mathCache);
112 for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
113 JS_ASSERT(!scriptsToGC[i]);
118 JSCompartment::init()
121 for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
123 for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
124 freeLists.finalizables[i] = NULL;
126 memset(&compartmentStats, 0, sizeof(JSGCArenaStats) * FINALIZE_LIMIT);
128 if (!crossCompartmentWrappers.init())
131 if (!propertyTree.init())
135 if (rt->meterEmptyShapes()) {
136 if (!emptyShapes.init())
141 if (!Shape::initEmptyShapes(this))
145 if (!InitJIT(&traceMonitor))
149 if (!toSourceCache.init())
153 regExpAllocator = JSC::ExecutableAllocator::create();
154 if (!regExpAllocator)
158 if (!backEdgeTable.init())
162 if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>()))
164 return jaegerCompartment->Initialize();
171 JSCompartment::arenaListsAreEmpty()
173 for (unsigned i = 0; i < FINALIZE_LIMIT; i++) {
174 if (!arenas[i].isEmpty())
181 IsCrossCompartmentWrapper(JSObject *wrapper)
183 return wrapper->isWrapper() &&
184 !!(JSWrapper::wrapperHandler(wrapper)->flags() & JSWrapper::CROSS_COMPARTMENT);
188 JSCompartment::wrap(JSContext *cx, Value *vp)
190 JS_ASSERT(cx->compartment == this);
194 JS_CHECK_RECURSION(cx, return false);
196 /* Only GC things have to be wrapped or copied. */
197 if (!vp->isMarkable())
200 if (vp->isString()) {
201 JSString *str = vp->toString();
203 /* Static strings do not have to be wrapped. */
204 if (JSString::isStatic(str))
207 /* If the string is already in this compartment, we are done. */
208 if (str->asCell()->compartment() == this)
211 /* If the string is an atom, we don't have to copy. */
212 if (str->isAtomized()) {
213 JS_ASSERT(str->asCell()->compartment() == cx->runtime->atomsCompartment);
219 * Wrappers should really be parented to the wrapped parent of the wrapped
220 * object, but in that case a wrapped global object would have a NULL
221 * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead
223 * we parent all wrappers to the global object in their home compartment.
224 * This loses us some transparency, and is generally very cheesy.
228 global = cx->fp()->scopeChain().getGlobal();
230 global = cx->globalObject;
231 OBJ_TO_INNER_OBJECT(cx, global);
236 /* Unwrap incoming objects. */
237 if (vp->isObject()) {
238 JSObject *obj = &vp->toObject();
240 /* If the object is already in this compartment, we are done. */
241 if (obj->compartment() == this)
244 /* Translate StopIteration singleton. */
245 if (obj->getClass() == &js_StopIterationClass)
246 return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp);
248 /* Don't unwrap an outer window proxy. */
249 if (!obj->getClass()->ext.innerObject) {
250 obj = vp->toObject().unwrap(&flags);
252 if (obj->getCompartment() == this)
255 if (cx->runtime->preWrapObjectCallback) {
256 obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
262 if (obj->getCompartment() == this)
265 if (cx->runtime->preWrapObjectCallback) {
266 obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
271 JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
277 JSObject *outer = obj;
278 OBJ_TO_OUTER_OBJECT(cx, outer);
279 JS_ASSERT(outer && outer == obj);
284 /* If we already have a wrapper for this value, use it. */
285 if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) {
287 if (vp->isObject()) {
288 JSObject *obj = &vp->toObject();
289 JS_ASSERT(IsCrossCompartmentWrapper(obj));
290 if (obj->getParent() != global) {
292 obj->setParent(global);
293 obj = obj->getProto();
294 } while (obj && IsCrossCompartmentWrapper(obj));
300 if (vp->isString()) {
302 JSString *str = vp->toString();
303 const jschar *chars = str->getChars(cx);
306 JSString *wrapped = js_NewStringCopyN(cx, chars, str->length());
309 vp->setString(wrapped);
310 return crossCompartmentWrappers.put(orig, *vp);
313 JSObject *obj = &vp->toObject();
316 * Recurse to wrap the prototype. Long prototype chains will run out of
317 * stack, causing an error in CHECK_RECURSE.
319 * Wrapping the proto before creating the new wrapper and adding it to the
320 * cache helps avoid leaving a bad entry in the cache on OOM. But note that
321 * if we wrapped both proto and parent, we would get infinite recursion
322 * here (since Object.prototype->parent->proto leads to Object.prototype
325 JSObject *proto = obj->getProto();
326 if (!wrap(cx, &proto))
330 * We hand in the original wrapped object into the wrap hook to allow
331 * the wrap hook to reason over what wrappers are currently applied
334 JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags);
338 vp->setObject(*wrapper);
340 wrapper->setProto(proto);
341 if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp))
344 wrapper->setParent(global);
349 JSCompartment::wrap(JSContext *cx, JSString **strp)
351 AutoValueRooter tvr(cx, StringValue(*strp));
352 if (!wrap(cx, tvr.addr()))
354 *strp = tvr.value().toString();
359 JSCompartment::wrap(JSContext *cx, JSObject **objp)
363 AutoValueRooter tvr(cx, ObjectValue(**objp));
364 if (!wrap(cx, tvr.addr()))
366 *objp = &tvr.value().toObject();
371 JSCompartment::wrapId(JSContext *cx, jsid *idp)
373 if (JSID_IS_INT(*idp))
375 AutoValueRooter tvr(cx, IdToValue(*idp));
376 if (!wrap(cx, tvr.addr()))
378 return ValueToId(cx, tvr.value(), idp);
382 JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
384 Value v = CastAsObjectJsval(*propp);
387 *propp = CastAsPropertyOp(v.toObjectOrNull());
392 JSCompartment::wrap(JSContext *cx, StrictPropertyOp *propp)
394 Value v = CastAsObjectJsval(*propp);
397 *propp = CastAsStrictPropertyOp(v.toObjectOrNull());
402 JSCompartment::wrap(JSContext *cx, PropertyDescriptor *desc)
404 return wrap(cx, &desc->obj) &&
405 (!(desc->attrs & JSPROP_GETTER) || wrap(cx, &desc->getter)) &&
406 (!(desc->attrs & JSPROP_SETTER) || wrap(cx, &desc->setter)) &&
407 wrap(cx, &desc->value);
411 JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
413 jsid *vector = props.begin();
414 jsint length = props.length();
415 for (size_t n = 0; n < size_t(length); ++n) {
416 if (!wrapId(cx, &vector[n]))
422 #if defined JS_METHODJIT && defined JS_MONOIC
424 * Check if the pool containing the code for jit should be destroyed, per the
425 * heuristics in JSCompartment::sweep.
428 ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
429 uint32 releaseInterval, uint32 &counter)
431 JSC::ExecutablePool *pool = jit->code.m_executablePool;
432 if (pool->m_gcNumber != cx->runtime->gcNumber) {
434 * The m_destroy flag may have been set in a previous GC for a pool which had
435 * references we did not remove (e.g. from the compartment's ExecutableAllocator)
436 * and is still around. Forget we tried to destroy it in such cases.
438 pool->m_destroy = false;
439 pool->m_gcNumber = cx->runtime->gcNumber;
440 if (--counter == 0) {
441 pool->m_destroy = true;
442 counter = releaseInterval;
445 return pool->m_destroy;
450 * This method marks pointers that cross compartment boundaries. It should be
451 * called only by per-compartment GCs, since full GCs naturally follow pointers
452 * across compartments.
455 JSCompartment::markCrossCompartment(JSTracer *trc)
457 for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
458 MarkValue(trc, e.front().key, "cross-compartment wrapper");
462 JSCompartment::mark(JSTracer *trc)
464 if (IS_GC_MARKING_TRACER(trc)) {
465 JSRuntime *rt = trc->context->runtime;
467 if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != this)
475 if (emptyArgumentsShape)
476 emptyArgumentsShape->trace(trc);
478 emptyBlockShape->trace(trc);
480 emptyCallShape->trace(trc);
481 if (emptyDeclEnvShape)
482 emptyDeclEnvShape->trace(trc);
483 if (emptyEnumeratorShape)
484 emptyEnumeratorShape->trace(trc);
486 emptyWithShape->trace(trc);
490 JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
493 /* Remove dead wrappers from the table. */
494 for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
495 JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&
496 !IsAboutToBeFinalized(cx, e.front().value.toGCThing()),
497 e.front().key.isString());
498 if (IsAboutToBeFinalized(cx, e.front().key.toGCThing()) ||
499 IsAboutToBeFinalized(cx, e.front().value.toGCThing())) {
505 traceMonitor.sweep(cx);
508 #if defined JS_METHODJIT && defined JS_MONOIC
511 * The release interval is the frequency with which we should try to destroy
512 * executable pools by releasing all JIT code in them, zero to never destroy pools.
513 * Initialize counter so that the first pool will be destroyed, and eventually drive
514 * the amount of JIT code in never-used compartments to zero. Don't discard anything
515 * for compartments which currently have active stack frames.
518 bool discardScripts = !active && releaseInterval != 0;
520 for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
521 JSScript *script = reinterpret_cast<JSScript *>(cursor);
522 if (script->hasJITCode()) {
523 mjit::ic::SweepCallICs(cx, script, discardScripts);
524 if (discardScripts) {
525 if (script->jitNormal &&
526 ScriptPoolDestroyed(cx, script->jitNormal, releaseInterval, counter)) {
527 mjit::ReleaseScriptCode(cx, script);
530 if (script->jitCtor &&
531 ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) {
532 mjit::ReleaseScriptCode(cx, script);
538 #endif /* JS_METHODJIT && JS_MONOIC */
544 JSCompartment::purge(JSContext *cx)
549 /* Destroy eval'ed scripts. */
550 js_DestroyScriptsToGC(cx, this);
552 nativeIterCache.purge();
553 toSourceCache.clear();
557 * If we are about to regenerate shapes, we have to flush the JIT cache,
558 * which will eventually abort any current recording.
560 if (cx->runtime->gcRegenShapes)
561 traceMonitor.needFlush = JS_TRUE;
565 for (JSScript *script = (JSScript *)scripts.next;
566 &script->links != &scripts;
567 script = (JSScript *)script->links.next) {
568 if (script->hasJITCode()) {
569 # if defined JS_POLYIC
570 mjit::ic::PurgePICs(cx, script);
572 # if defined JS_MONOIC
574 * MICs do not refer to data which can be GC'ed and do not generate stubs
575 * which might need to be discarded, but are sensitive to shape regeneration.
577 if (cx->runtime->gcRegenShapes)
578 mjit::ic::PurgeMICs(cx, script);
586 JSCompartment::allocMathCache(JSContext *cx)
588 JS_ASSERT(!mathCache);
589 mathCache = js_new<MathCache>();
591 js_ReportOutOfMemory(cx);
596 JSCompartment::backEdgeCount(jsbytecode *pc) const
598 if (BackEdgeMap::Ptr p = backEdgeTable.lookup(pc))
605 JSCompartment::incBackEdgeCount(jsbytecode *pc)
607 if (BackEdgeMap::AddPtr p = backEdgeTable.lookupForAdd(pc)) {
611 backEdgeTable.add(p, pc, 1);