Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jscompartment.cpp
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 "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"
53
54 #include "jsgcinlines.h"
55
56 #if ENABLE_YARR_JIT
57 #include "assembler/jit/ExecutableAllocator.h"
58 #endif
59
60 using namespace js;
61 using namespace js::gc;
62
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(thisForCtor()),
75     debugMode(rt->debugMode),
76 #if ENABLE_YARR_JIT
77     regExpAllocator(NULL),
78 #endif
79     mathCache(NULL),
80     marked(false)
81 {
82     JS_INIT_CLIST(&scripts);
83
84 #ifdef JS_TRACER
85     /* InitJIT expects this area to be zero'd. */
86     PodZero(&traceMonitor);
87 #endif
88
89     PodArrayZero(scriptsToGC);
90 }
91
92 JSCompartment::~JSCompartment()
93 {
94     Shape::finishEmptyShapes(this);
95     propertyTree.finish();
96
97 #if ENABLE_YARR_JIT
98     js_delete(regExpAllocator);
99 #endif
100
101 #if defined JS_TRACER
102     FinishJIT(&traceMonitor);
103 #endif
104
105 #ifdef JS_METHODJIT
106     js_delete(jaegerCompartment);
107 #endif
108
109     js_delete(mathCache);
110
111 #ifdef DEBUG
112     for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
113         JS_ASSERT(!scriptsToGC[i]);
114 #endif
115 }
116
117 bool
118 JSCompartment::init()
119 {
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;
130
131     if (!propertyTree.init())
132         return false;
133
134 #ifdef DEBUG
135     if (rt->meterEmptyShapes()) {
136         if (!emptyShapes.init())
137             return false;
138     }
139 #endif
140
141     if (!Shape::initEmptyShapes(this))
142         return false;
143
144 #ifdef JS_TRACER
145     if (!InitJIT(&traceMonitor))
146         return false;
147 #endif
148
149     if (!toSourceCache.init())
150         return false;
151
152 #if ENABLE_YARR_JIT
153     regExpAllocator = JSC::ExecutableAllocator::create();
154     if (!regExpAllocator)
155         return false;
156 #endif
157
158     if (!backEdgeTable.init())
159         return false;
160
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
168 }
169
170 bool
171 JSCompartment::arenaListsAreEmpty()
172 {
173   for (unsigned i = 0; i < FINALIZE_LIMIT; i++) {
174        if (!arenas[i].isEmpty())
175            return false;
176   }
177   return true;
178 }
179
180 static bool
181 IsCrossCompartmentWrapper(JSObject *wrapper)
182 {
183     return wrapper->isWrapper() &&
184            !!(JSWrapper::wrapperHandler(wrapper)->flags() & JSWrapper::CROSS_COMPARTMENT);
185 }
186
187 bool
188 JSCompartment::wrap(JSContext *cx, Value *vp)
189 {
190     JS_ASSERT(cx->compartment == this);
191
192     uintN flags = 0;
193
194     JS_CHECK_RECURSION(cx, return false);
195
196     /* Only GC things have to be wrapped or copied. */
197     if (!vp->isMarkable())
198         return true;
199
200     if (vp->isString()) {
201         JSString *str = vp->toString();
202
203         /* Static strings do not have to be wrapped. */
204         if (JSString::isStatic(str))
205             return true;
206
207         /* If the string is already in this compartment, we are done. */
208         if (str->asCell()->compartment() == this)
209             return true;
210
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;
215         }
216     }
217
218     /*
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
222 ,
223      * we parent all wrappers to the global object in their home compartment.
224      * This loses us some transparency, and is generally very cheesy.
225      */
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;
234     }
235
236     /* Unwrap incoming objects. */
237     if (vp->isObject()) {
238         JSObject *obj = &vp->toObject();
239
240         /* If the object is already in this compartment, we are done. */
241         if (obj->compartment() == this)
242             return true;
243
244         /* Translate StopIteration singleton. */
245         if (obj->getClass() == &js_StopIterationClass)
246             return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp);
247
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;
254
255             if (cx->runtime->preWrapObjectCallback) {
256                 obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
257                 if (!obj)
258                     return false;
259             }
260
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;
269             }
270
271             JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
272             vp->setObject(*obj);
273         }
274
275 #ifdef DEBUG
276         {
277             JSObject *outer = obj;
278             OBJ_TO_OUTER_OBJECT(cx, outer);
279             JS_ASSERT(outer && outer == obj);
280         }
281 #endif
282     }
283
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));
295             }
296         }
297         return true;
298     }
299
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);
311     }
312
313     JSObject *obj = &vp->toObject();
314
315     /*
316      * Recurse to wrap the prototype. Long prototype chains will run out of
317      * stack, causing an error in CHECK_RECURSE.
318      *
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).
324      */
325     JSObject *proto = obj->getProto();
326     if (!wrap(cx, &proto))
327         return false;
328
329     /*
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.
333      */
334     JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags);
335     if (!wrapper)
336         return false;
337
338     vp->setObject(*wrapper);
339
340     wrapper->setProto(proto);
341     if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp))
342         return false;
343
344     wrapper->setParent(global);
345     return true;
346 }
347
348 bool
349 JSCompartment::wrap(JSContext *cx, JSString **strp)
350 {
351     AutoValueRooter tvr(cx, StringValue(*strp));
352     if (!wrap(cx, tvr.addr()))
353         return false;
354     *strp = tvr.value().toString();
355     return true;
356 }
357
358 bool
359 JSCompartment::wrap(JSContext *cx, JSObject **objp)
360 {
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;
368 }
369
370 bool
371 JSCompartment::wrapId(JSContext *cx, jsid *idp)
372 {
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);
379 }
380
381 bool
382 JSCompartment::wrap(JSContext *cx, PropertyOp *propp)
383 {
384     Value v = CastAsObjectJsval(*propp);
385     if (!wrap(cx, &v))
386         return false;
387     *propp = CastAsPropertyOp(v.toObjectOrNull());
388     return true;
389 }
390
391 bool
392 JSCompartment::wrap(JSContext *cx, StrictPropertyOp *propp)
393 {
394     Value v = CastAsObjectJsval(*propp);
395     if (!wrap(cx, &v))
396         return false;
397     *propp = CastAsStrictPropertyOp(v.toObjectOrNull());
398     return true;
399 }
400
401 bool
402 JSCompartment::wrap(JSContext *cx, PropertyDescriptor *desc)
403 {
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);
408 }
409
410 bool
411 JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
412 {
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;
418     }
419     return true;
420 }
421
422 #if defined JS_METHODJIT && defined JS_MONOIC
423 /*
424  * Check if the pool containing the code for jit should be destroyed, per the
425  * heuristics in JSCompartment::sweep.
426  */
427 static inline bool
428 ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
429                     uint32 releaseInterval, uint32 &counter)
430 {
431     JSC::ExecutablePool *pool = jit->code.m_executablePool;
432     if (pool->m_gcNumber != cx->runtime->gcNumber) {
433         /*
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.
437          */
438         pool->m_destroy = false;
439         pool->m_gcNumber = cx->runtime->gcNumber;
440         if (--counter == 0) {
441             pool->m_destroy = true;
442             counter = releaseInterval;
443         }
444     }
445     return pool->m_destroy;
446 }
447 #endif
448
449 /*
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.
453  */
454 void
455 JSCompartment::markCrossCompartment(JSTracer *trc)
456 {
457     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
458         MarkValue(trc, e.front().key, "cross-compartment wrapper");
459 }
460
461 void
462 JSCompartment::mark(JSTracer *trc)
463 {
464     if (IS_GC_MARKING_TRACER(trc)) {
465         JSRuntime *rt = trc->context->runtime;
466
467         if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != this)
468             return;
469
470         if (marked)
471             return;
472         marked = true;
473     }
474
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);
487 }
488
489 void
490 JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
491 {
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();
501         }
502     }
503
504 #ifdef JS_TRACER
505     traceMonitor.sweep(cx);
506 #endif
507
508 #if defined JS_METHODJIT && defined JS_MONOIC
509
510     /*
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.
516      */
517     uint32 counter = 1;
518     bool discardScripts = !active && releaseInterval != 0;
519
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;
529                 }
530                 if (script->jitCtor &&
531                     ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) {
532                     mjit::ReleaseScriptCode(cx, script);
533                 }
534             }
535         }
536     }
537
538 #endif /* JS_METHODJIT && JS_MONOIC */
539
540     active = false;
541 }
542
543 void
544 JSCompartment::purge(JSContext *cx)
545 {
546     freeLists.purge();
547     dtoaCache.purge();
548
549     /* Destroy eval'ed scripts. */
550     js_DestroyScriptsToGC(cx, this);
551
552     nativeIterCache.purge();
553     toSourceCache.clear();
554
555 #ifdef JS_TRACER
556     /*
557      * If we are about to regenerate shapes, we have to flush the JIT cache,
558      * which will eventually abort any current recording.
559      */
560     if (cx->runtime->gcRegenShapes)
561         traceMonitor.needFlush = JS_TRUE;
562 #endif
563
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
573             /*
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.
576              */
577             if (cx->runtime->gcRegenShapes)
578                 mjit::ic::PurgeMICs(cx, script);
579 # endif
580         }
581     }
582 #endif
583 }
584
585 MathCache *
586 JSCompartment::allocMathCache(JSContext *cx)
587 {
588     JS_ASSERT(!mathCache);
589     mathCache = js_new<MathCache>();
590     if (!mathCache)
591         js_ReportOutOfMemory(cx);
592     return mathCache;
593 }
594
595 size_t
596 JSCompartment::backEdgeCount(jsbytecode *pc) const
597 {
598     if (BackEdgeMap::Ptr p = backEdgeTable.lookup(pc))
599         return p->value;
600
601     return 0;
602 }
603
604 size_t
605 JSCompartment::incBackEdgeCount(jsbytecode *pc)
606 {
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;
613     }
614 }
615