Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jscompartment.cpp
blobaeeb0e2c96648b70de495805bc0965d0171d857e
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
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
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.
25 * Contributor(s):
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 ***** */
41 #include "jscntxt.h"
42 #include "jscompartment.h"
43 #include "jsgc.h"
44 #include "jsiter.h"
45 #include "jsproxy.h"
46 #include "jsscope.h"
47 #include "jstracer.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"
56 #if ENABLE_YARR_JIT
57 #include "assembler/jit/ExecutableAllocator.h"
58 #endif
60 using namespace js;
61 using namespace js::gc;
63 JSCompartment::JSCompartment(JSRuntime *rt)
64 : rt(rt),
65 principals(NULL),
66 gcBytes(0),
67 gcTriggerBytes(0),
68 gcLastBytes(0),
69 data(NULL),
70 active(false),
71 #ifdef JS_METHODJIT
72 jaegerCompartment(NULL),
73 #endif
74 propertyTree(this),
75 debugMode(rt->debugMode),
76 #if ENABLE_YARR_JIT
77 regExpAllocator(NULL),
78 #endif
79 mathCache(NULL),
80 marked(false)
82 JS_INIT_CLIST(&scripts);
84 #ifdef JS_TRACER
85 /* InitJIT expects this area to be zero'd. */
86 PodZero(&traceMonitor);
87 #endif
89 PodArrayZero(scriptsToGC);
92 JSCompartment::~JSCompartment()
94 Shape::finishEmptyShapes(this);
95 propertyTree.finish();
97 #if ENABLE_YARR_JIT
98 js_delete(regExpAllocator);
99 #endif
101 #if defined JS_TRACER
102 FinishJIT(&traceMonitor);
103 #endif
105 #ifdef JS_METHODJIT
106 js_delete(jaegerCompartment);
107 #endif
109 js_delete(mathCache);
111 #ifdef DEBUG
112 for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
113 JS_ASSERT(!scriptsToGC[i]);
114 #endif
117 bool
118 JSCompartment::init()
120 chunk = NULL;
121 for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
122 arenas[i].init();
123 for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
124 freeLists.finalizables[i] = NULL;
125 #ifdef JS_GCMETER
126 memset(&compartmentStats, 0, sizeof(JSGCArenaStats) * FINALIZE_LIMIT);
127 #endif
128 if (!crossCompartmentWrappers.init())
129 return false;
131 if (!propertyTree.init())
132 return false;
134 #ifdef DEBUG
135 if (rt->meterEmptyShapes()) {
136 if (!emptyShapes.init())
137 return false;
139 #endif
141 if (!Shape::initEmptyShapes(this))
142 return false;
144 #ifdef JS_TRACER
145 if (!InitJIT(&traceMonitor))
146 return false;
147 #endif
149 if (!toSourceCache.init())
150 return false;
152 #if ENABLE_YARR_JIT
153 regExpAllocator = JSC::ExecutableAllocator::create();
154 if (!regExpAllocator)
155 return false;
156 #endif
158 if (!backEdgeTable.init())
159 return false;
161 #ifdef JS_METHODJIT
162 if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>()))
163 return false;
164 return jaegerCompartment->Initialize();
165 #else
166 return true;
167 #endif
170 bool
171 JSCompartment::arenaListsAreEmpty()
173 for (unsigned i = 0; i < FINALIZE_LIMIT; i++) {
174 if (!arenas[i].isEmpty())
175 return false;
177 return true;
180 static bool
181 IsCrossCompartmentWrapper(JSObject *wrapper)
183 return wrapper->isWrapper() &&
184 !!(JSWrapper::wrapperHandler(wrapper)->flags() & JSWrapper::CROSS_COMPARTMENT);
187 bool
188 JSCompartment::wrap(JSContext *cx, Value *vp)
190 JS_ASSERT(cx->compartment == this);
192 uintN flags = 0;
194 JS_CHECK_RECURSION(cx, return false);
196 /* Only GC things have to be wrapped or copied. */
197 if (!vp->isMarkable())
198 return true;
200 if (vp->isString()) {
201 JSString *str = vp->toString();
203 /* Static strings do not have to be wrapped. */
204 if (JSString::isStatic(str))
205 return true;
207 /* If the string is already in this compartment, we are done. */
208 if (str->asCell()->compartment() == this)
209 return true;
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);
214 return true;
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.
226 JSObject *global;
227 if (cx->hasfp()) {
228 global = cx->fp()->scopeChain().getGlobal();
229 } else {
230 global = cx->globalObject;
231 OBJ_TO_INNER_OBJECT(cx, global);
232 if (!global)
233 return false;
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)
242 return true;
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);
251 vp->setObject(*obj);
252 if (obj->getCompartment() == this)
253 return true;
255 if (cx->runtime->preWrapObjectCallback) {
256 obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
257 if (!obj)
258 return false;
261 vp->setObject(*obj);
262 if (obj->getCompartment() == this)
263 return true;
264 } else {
265 if (cx->runtime->preWrapObjectCallback) {
266 obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
267 if (!obj)
268 return false;
271 JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
272 vp->setObject(*obj);
275 #ifdef DEBUG
277 JSObject *outer = obj;
278 OBJ_TO_OUTER_OBJECT(cx, outer);
279 JS_ASSERT(outer && outer == obj);
281 #endif
284 /* If we already have a wrapper for this value, use it. */
285 if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) {
286 *vp = p->value;
287 if (vp->isObject()) {
288 JSObject *obj = &vp->toObject();
289 JS_ASSERT(IsCrossCompartmentWrapper(obj));
290 if (obj->getParent() != global) {
291 do {
292 obj->setParent(global);
293 obj = obj->getProto();
294 } while (obj && IsCrossCompartmentWrapper(obj));
297 return true;
300 if (vp->isString()) {
301 Value orig = *vp;
302 JSString *str = vp->toString();
303 const jschar *chars = str->getChars(cx);
304 if (!chars)
305 return false;
306 JSString *wrapped = js_NewStringCopyN(cx, chars, str->length());
307 if (!wrapped)
308 return false;
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
323 * itself).
325 JSObject *proto = obj->getProto();
326 if (!wrap(cx, &proto))
327 return false;
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
332 * to the object.
334 JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags);
335 if (!wrapper)
336 return false;
338 vp->setObject(*wrapper);
340 wrapper->setProto(proto);
341 if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp))
342 return false;
344 wrapper->setParent(global);
345 return true;
348 bool
349 JSCompartment::wrap(JSContext *cx, JSString **strp)
351 AutoValueRooter tvr(cx, StringValue(*strp));
352 if (!wrap(cx, tvr.addr()))
353 return false;
354 *strp = tvr.value().toString();
355 return true;
358 bool
359 JSCompartment::wrap(JSContext *cx, JSObject **objp)
361 if (!*objp)
362 return true;
363 AutoValueRooter tvr(cx, ObjectValue(**objp));
364 if (!wrap(cx, tvr.addr()))
365 return false;
366 *objp = &tvr.value().toObject();
367 return true;
370 bool
371 JSCompartment::wrapId(JSContext *cx, jsid *idp)
373 if (JSID_IS_INT(*idp))
374 return true;
375 AutoValueRooter tvr(cx, IdToValue(*idp));
376 if (!wrap(cx, tvr.addr()))
377 return false;
378 return ValueToId(cx, tvr.value(), idp);
381 bool
382 JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
384 Value v = CastAsObjectJsval(*propp);
385 if (!wrap(cx, &v))
386 return false;
387 *propp = CastAsPropertyOp(v.toObjectOrNull());
388 return true;
391 bool
392 JSCompartment::wrap(JSContext *cx, StrictPropertyOp *propp)
394 Value v = CastAsObjectJsval(*propp);
395 if (!wrap(cx, &v))
396 return false;
397 *propp = CastAsStrictPropertyOp(v.toObjectOrNull());
398 return true;
401 bool
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);
410 bool
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]))
417 return false;
419 return true;
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.
427 static inline bool
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;
447 #endif
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.
454 void
455 JSCompartment::markCrossCompartment(JSTracer *trc)
457 for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
458 MarkValue(trc, e.front().key, "cross-compartment wrapper");
461 void
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)
467 return;
469 if (marked)
470 return;
472 marked = true;
475 if (emptyArgumentsShape)
476 emptyArgumentsShape->trace(trc);
477 if (emptyBlockShape)
478 emptyBlockShape->trace(trc);
479 if (emptyCallShape)
480 emptyCallShape->trace(trc);
481 if (emptyDeclEnvShape)
482 emptyDeclEnvShape->trace(trc);
483 if (emptyEnumeratorShape)
484 emptyEnumeratorShape->trace(trc);
485 if (emptyWithShape)
486 emptyWithShape->trace(trc);
489 void
490 JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
492 chunk = NULL;
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())) {
500 e.removeFront();
504 #ifdef JS_TRACER
505 traceMonitor.sweep(cx);
506 #endif
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.
517 uint32 counter = 1;
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);
528 continue;
530 if (script->jitCtor &&
531 ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) {
532 mjit::ReleaseScriptCode(cx, script);
538 #endif /* JS_METHODJIT && JS_MONOIC */
540 active = false;
543 void
544 JSCompartment::purge(JSContext *cx)
546 freeLists.purge();
547 dtoaCache.purge();
549 /* Destroy eval'ed scripts. */
550 js_DestroyScriptsToGC(cx, this);
552 nativeIterCache.purge();
553 toSourceCache.clear();
555 #ifdef JS_TRACER
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;
562 #endif
564 #ifdef JS_METHODJIT
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);
571 # endif
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);
579 # endif
582 #endif
585 MathCache *
586 JSCompartment::allocMathCache(JSContext *cx)
588 JS_ASSERT(!mathCache);
589 mathCache = js_new<MathCache>();
590 if (!mathCache)
591 js_ReportOutOfMemory(cx);
592 return mathCache;
595 size_t
596 JSCompartment::backEdgeCount(jsbytecode *pc) const
598 if (BackEdgeMap::Ptr p = backEdgeTable.lookup(pc))
599 return p->value;
601 return 0;
604 size_t
605 JSCompartment::incBackEdgeCount(jsbytecode *pc)
607 if (BackEdgeMap::AddPtr p = backEdgeTable.lookupForAdd(pc)) {
608 p->value++;
609 return p->value;
610 } else {
611 backEdgeTable.add(p, pc, 1);
612 return 1;