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
),
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
;
466 if (rt
->gcCurrentCompartment
!= NULL
&& 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);