Bug 617935: Check string lengths using StringBuffer. (r=lw)
[mozilla-central.git] / js / src / jscntxt.cpp
blob24e7cebcd2f6a35126891bae5524693e98090a95
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=80:
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 Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
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 ***** */
42 * JS execution context.
44 #include <new>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #ifdef ANDROID
49 # include <android/log.h>
50 # include <fstream>
51 # include <string>
52 #endif // ANDROID
54 #include "jsstdint.h"
56 #include "jstypes.h"
57 #include "jsarena.h"
58 #include "jsutil.h"
59 #include "jsclist.h"
60 #include "jsprf.h"
61 #include "jsatom.h"
62 #include "jscntxt.h"
63 #include "jsversion.h"
64 #include "jsdbgapi.h"
65 #include "jsexn.h"
66 #include "jsfun.h"
67 #include "jsgc.h"
68 #include "jsiter.h"
69 #include "jslock.h"
70 #include "jsmath.h"
71 #include "jsnativestack.h"
72 #include "jsnum.h"
73 #include "jsobj.h"
74 #include "jsopcode.h"
75 #include "jspubtd.h"
76 #include "jsscan.h"
77 #include "jsscope.h"
78 #include "jsscript.h"
79 #include "jsstaticcheck.h"
80 #include "jsstr.h"
81 #include "jstracer.h"
83 #ifdef JS_METHODJIT
84 # include "assembler/assembler/MacroAssembler.h"
85 #endif
87 #include "jscntxtinlines.h"
88 #include "jscompartment.h"
89 #include "jsinterpinlines.h"
90 #include "jsobjinlines.h"
92 #ifdef XP_WIN
93 # include "jswin.h"
94 #elif defined(XP_OS2)
95 # define INCL_DOSMEMMGR
96 # include <os2.h>
97 #else
98 # include <unistd.h>
99 # include <sys/mman.h>
100 # if !defined(MAP_ANONYMOUS)
101 # if defined(MAP_ANON)
102 # define MAP_ANONYMOUS MAP_ANON
103 # else
104 # define MAP_ANONYMOUS 0
105 # endif
106 # endif
107 #endif
109 using namespace js;
110 using namespace js::gc;
112 static const size_t ARENA_HEADER_SIZE_HACK = 40;
113 static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
115 static void
116 FreeContext(JSContext *cx);
118 #ifdef DEBUG
119 JS_REQUIRES_STACK bool
120 StackSegment::contains(const JSStackFrame *fp) const
122 JS_ASSERT(inContext());
123 JSStackFrame *start;
124 JSStackFrame *stop;
125 if (isActive()) {
126 JS_ASSERT(cx->hasfp());
127 start = cx->fp();
128 stop = cx->activeSegment()->initialFrame->prev();
129 } else {
130 JS_ASSERT(suspendedRegs && suspendedRegs->fp);
131 start = suspendedRegs->fp;
132 stop = initialFrame->prev();
134 for (JSStackFrame *f = start; f != stop; f = f->prev()) {
135 if (f == fp)
136 return true;
138 return false;
140 #endif
142 bool
143 StackSpace::init()
145 void *p;
146 #ifdef XP_WIN
147 p = VirtualAlloc(NULL, CAPACITY_BYTES, MEM_RESERVE, PAGE_READWRITE);
148 if (!p)
149 return false;
150 void *check = VirtualAlloc(p, COMMIT_BYTES, MEM_COMMIT, PAGE_READWRITE);
151 if (p != check)
152 return false;
153 base = reinterpret_cast<Value *>(p);
154 commitEnd = base + COMMIT_VALS;
155 end = base + CAPACITY_VALS;
156 #elif defined(XP_OS2)
157 if (DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY) &&
158 DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE))
159 return false;
160 base = reinterpret_cast<Value *>(p);
161 end = base + CAPACITY_VALS;
162 #else
163 JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
164 p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
165 if (p == MAP_FAILED)
166 return false;
167 base = reinterpret_cast<Value *>(p);
168 end = base + CAPACITY_VALS;
169 #endif
170 return true;
173 void
174 StackSpace::finish()
176 #ifdef XP_WIN
177 VirtualFree(base, (commitEnd - base) * sizeof(Value), MEM_DECOMMIT);
178 VirtualFree(base, 0, MEM_RELEASE);
179 #elif defined(XP_OS2)
180 DosFreeMem(base);
181 #else
182 #ifdef SOLARIS
183 munmap((caddr_t)base, CAPACITY_BYTES);
184 #else
185 munmap(base, CAPACITY_BYTES);
186 #endif
187 #endif
190 #ifdef XP_WIN
191 JS_FRIEND_API(bool)
192 StackSpace::bumpCommit(Value *from, ptrdiff_t nvals) const
194 JS_ASSERT(end - from >= nvals);
195 Value *newCommit = commitEnd;
196 Value *request = from + nvals;
198 /* Use a dumb loop; will probably execute once. */
199 JS_ASSERT((end - newCommit) % COMMIT_VALS == 0);
200 do {
201 newCommit += COMMIT_VALS;
202 JS_ASSERT((end - newCommit) >= 0);
203 } while (newCommit < request);
205 /* The cast is safe because CAPACITY_BYTES is small. */
206 int32 size = static_cast<int32>(newCommit - commitEnd) * sizeof(Value);
208 if (!VirtualAlloc(commitEnd, size, MEM_COMMIT, PAGE_READWRITE))
209 return false;
210 commitEnd = newCommit;
211 return true;
213 #endif
215 void
216 StackSpace::mark(JSTracer *trc)
219 * The correctness/completeness of marking depends on the continuity
220 * invariants described by the StackSegment and StackSpace definitions.
222 * NB:
223 * Stack slots might be torn or uninitialized in the presence of method
224 * JIT'd code. Arguments are an exception and are always fully synced
225 * (so they can be read by functions).
227 Value *end = firstUnused();
228 for (StackSegment *seg = currentSegment; seg; seg = seg->getPreviousInMemory()) {
229 STATIC_ASSERT(ubound(end) >= 0);
230 if (seg->inContext()) {
231 /* This may be the only pointer to the initialVarObj. */
232 if (seg->hasInitialVarObj())
233 MarkObject(trc, seg->getInitialVarObj(), "varobj");
235 /* Mark slots/args trailing off of the last stack frame. */
236 JSStackFrame *fp = seg->getCurrentFrame();
237 MarkStackRangeConservatively(trc, fp->slots(), end);
239 /* Mark stack frames and slots/args between stack frames. */
240 JSStackFrame *initial = seg->getInitialFrame();
241 for (JSStackFrame *f = fp; f != initial; f = f->prev()) {
242 js_TraceStackFrame(trc, f);
243 MarkStackRangeConservatively(trc, f->prev()->slots(), (Value *)f);
246 /* Mark initial stack frame and leading args. */
247 js_TraceStackFrame(trc, initial);
248 MarkStackRangeConservatively(trc, seg->valueRangeBegin(), (Value *)initial);
249 } else {
250 /* Mark slots/args trailing off segment. */
251 MarkValueRange(trc, seg->valueRangeBegin(), end, "stack");
253 end = (Value *)seg;
257 bool
258 StackSpace::pushSegmentForInvoke(JSContext *cx, uintN argc, InvokeArgsGuard *ag)
260 Value *start = firstUnused();
261 ptrdiff_t nvals = VALUES_PER_STACK_SEGMENT + 2 + argc;
262 if (!ensureSpace(cx, start, nvals))
263 return false;
265 StackSegment *seg = new(start) StackSegment;
266 seg->setPreviousInMemory(currentSegment);
267 currentSegment = seg;
269 ag->cx = cx;
270 ag->seg = seg;
271 ag->argv_ = seg->valueRangeBegin() + 2;
272 ag->argc_ = argc;
274 /* Use invokeArgEnd to root [vp, vpend) until the frame is pushed. */
275 #ifdef DEBUG
276 ag->prevInvokeSegment = invokeSegment;
277 invokeSegment = seg;
278 ag->prevInvokeFrame = invokeFrame;
279 invokeFrame = NULL;
280 #endif
281 ag->prevInvokeArgEnd = invokeArgEnd;
282 invokeArgEnd = ag->argv() + ag->argc();
283 return true;
286 void
287 StackSpace::popSegmentForInvoke(const InvokeArgsGuard &ag)
289 JS_ASSERT(!currentSegment->inContext());
290 JS_ASSERT(ag.seg == currentSegment);
291 JS_ASSERT(invokeSegment == currentSegment);
292 JS_ASSERT(invokeArgEnd == ag.argv() + ag.argc());
294 currentSegment = currentSegment->getPreviousInMemory();
296 #ifdef DEBUG
297 invokeSegment = ag.prevInvokeSegment;
298 invokeFrame = ag.prevInvokeFrame;
299 #endif
300 invokeArgEnd = ag.prevInvokeArgEnd;
303 bool
304 StackSpace::getSegmentAndFrame(JSContext *cx, uintN vplen, uintN nfixed,
305 FrameGuard *fg) const
307 Value *start = firstUnused();
308 uintN nvals = VALUES_PER_STACK_SEGMENT + vplen + VALUES_PER_STACK_FRAME + nfixed;
309 if (!ensureSpace(cx, start, nvals))
310 return false;
312 fg->seg_ = new(start) StackSegment;
313 fg->vp_ = start + VALUES_PER_STACK_SEGMENT;
314 fg->fp_ = reinterpret_cast<JSStackFrame *>(fg->vp() + vplen);
315 return true;
318 void
319 StackSpace::pushSegmentAndFrame(JSContext *cx, JSObject *initialVarObj,
320 JSFrameRegs *regs, FrameGuard *fg)
322 /* Caller should have already initialized regs. */
323 JS_ASSERT(regs->fp == fg->fp());
325 /* Push segment. */
326 StackSegment *seg = fg->segment();
327 seg->setPreviousInMemory(currentSegment);
328 currentSegment = seg;
330 /* Push frame. */
331 cx->pushSegmentAndFrame(seg, *regs);
332 seg->setInitialVarObj(initialVarObj);
333 fg->cx_ = cx;
336 void
337 StackSpace::popSegmentAndFrame(JSContext *cx)
339 JS_ASSERT(isCurrentAndActive(cx));
340 JS_ASSERT(cx->hasActiveSegment());
341 cx->popSegmentAndFrame();
342 currentSegment = currentSegment->getPreviousInMemory();
345 FrameGuard::~FrameGuard()
347 if (!pushed())
348 return;
349 JS_ASSERT(cx_->activeSegment() == segment());
350 JS_ASSERT(cx_->maybefp() == fp());
351 cx_->stack().popSegmentAndFrame(cx_);
354 bool
355 StackSpace::getExecuteFrame(JSContext *cx, JSScript *script, ExecuteFrameGuard *fg) const
357 return getSegmentAndFrame(cx, 2, script->nfixed, fg);
360 void
361 StackSpace::pushExecuteFrame(JSContext *cx, JSObject *initialVarObj, ExecuteFrameGuard *fg)
363 JSStackFrame *fp = fg->fp();
364 JSScript *script = fp->script();
365 fg->regs_.pc = script->code;
366 fg->regs_.fp = fp;
367 fg->regs_.sp = fp->base();
368 pushSegmentAndFrame(cx, initialVarObj, &fg->regs_, fg);
371 bool
372 StackSpace::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *fg)
374 if (!getSegmentAndFrame(cx, 0 /*vplen*/, 0 /*nfixed*/, fg))
375 return false;
376 fg->fp()->initDummyFrame(cx, scopeChain);
377 fg->regs_.fp = fg->fp();
378 fg->regs_.pc = NULL;
379 fg->regs_.sp = fg->fp()->slots();
380 pushSegmentAndFrame(cx, NULL /*varobj*/, &fg->regs_, fg);
381 return true;
384 bool
385 StackSpace::getGeneratorFrame(JSContext *cx, uintN vplen, uintN nfixed, GeneratorFrameGuard *fg)
387 return getSegmentAndFrame(cx, vplen, nfixed, fg);
390 void
391 StackSpace::pushGeneratorFrame(JSContext *cx, JSFrameRegs *regs, GeneratorFrameGuard *fg)
393 JS_ASSERT(regs->fp == fg->fp());
394 JS_ASSERT(regs->fp->prev() == cx->maybefp());
395 pushSegmentAndFrame(cx, NULL /*varobj*/, regs, fg);
398 bool
399 StackSpace::bumpCommitAndLimit(JSStackFrame *base, Value *sp, uintN nvals, Value **limit) const
401 JS_ASSERT(sp >= firstUnused());
402 JS_ASSERT(sp + nvals >= *limit);
403 #ifdef XP_WIN
404 if (commitEnd <= *limit) {
405 Value *quotaEnd = (Value *)base + STACK_QUOTA;
406 if (sp + nvals < quotaEnd) {
407 if (!ensureSpace(NULL, sp, nvals))
408 return false;
409 *limit = Min(quotaEnd, commitEnd);
410 return true;
413 #endif
414 return false;
417 void
418 FrameRegsIter::initSlow()
420 if (!curseg) {
421 curfp = NULL;
422 cursp = NULL;
423 curpc = NULL;
424 return;
427 JS_ASSERT(curseg->isSuspended());
428 curfp = curseg->getSuspendedFrame();
429 cursp = curseg->getSuspendedRegs()->sp;
430 curpc = curseg->getSuspendedRegs()->pc;
434 * Using the invariant described in the js::StackSegment comment, we know that,
435 * when a pair of prev-linked stack frames are in the same segment, the
436 * first frame's address is the top of the prev-frame's stack, modulo missing
437 * arguments.
439 void
440 FrameRegsIter::incSlow(JSStackFrame *fp, JSStackFrame *prev)
442 JS_ASSERT(prev);
443 JS_ASSERT(curpc == curfp->pc(cx, fp));
444 JS_ASSERT(fp == curseg->getInitialFrame());
447 * If fp is in cs and the prev-frame is in csprev, it is not necessarily
448 * the case that |cs->getPreviousInContext == csprev| or that
449 * |csprev->getSuspendedFrame == prev| (because of indirect eval and
450 * JS_EvaluateInStackFrame). To compute prev's sp, we need to do a linear
451 * scan, keeping track of what is immediately after prev in memory.
453 curseg = curseg->getPreviousInContext();
454 cursp = curseg->getSuspendedRegs()->sp;
455 JSStackFrame *f = curseg->getSuspendedFrame();
456 while (f != prev) {
457 if (f == curseg->getInitialFrame()) {
458 curseg = curseg->getPreviousInContext();
459 cursp = curseg->getSuspendedRegs()->sp;
460 f = curseg->getSuspendedFrame();
461 } else {
462 cursp = f->formalArgsEnd();
463 f = f->prev();
468 AllFramesIter::AllFramesIter(JSContext *cx)
469 : curcs(cx->stack().getCurrentSegment()),
470 curfp(curcs ? curcs->getCurrentFrame() : NULL)
474 AllFramesIter&
475 AllFramesIter::operator++()
477 JS_ASSERT(!done());
478 if (curfp == curcs->getInitialFrame()) {
479 curcs = curcs->getPreviousInMemory();
480 curfp = curcs ? curcs->getCurrentFrame() : NULL;
481 } else {
482 curfp = curfp->prev();
484 return *this;
487 bool
488 JSThreadData::init()
490 #ifdef DEBUG
491 /* The data must be already zeroed. */
492 for (size_t i = 0; i != sizeof(*this); ++i)
493 JS_ASSERT(reinterpret_cast<uint8*>(this)[i] == 0);
494 #endif
495 if (!stackSpace.init())
496 return false;
497 dtoaState = js_NewDtoaState();
498 if (!dtoaState) {
499 finish();
500 return false;
502 nativeStackBase = GetNativeStackBase();
504 #ifdef JS_TRACER
505 /* Set the default size for the code cache to 16MB. */
506 maxCodeCacheBytes = 16 * 1024 * 1024;
507 #endif
509 return true;
512 void
513 JSThreadData::finish()
515 if (dtoaState)
516 js_DestroyDtoaState(dtoaState);
518 js_FinishGSNCache(&gsnCache);
519 propertyCache.~PropertyCache();
520 stackSpace.finish();
523 void
524 JSThreadData::mark(JSTracer *trc)
526 stackSpace.mark(trc);
529 void
530 JSThreadData::purge(JSContext *cx)
532 js_PurgeGSNCache(&gsnCache);
534 /* FIXME: bug 506341. */
535 propertyCache.purge(cx);
537 dtoaCache.s = NULL;
540 #ifdef JS_THREADSAFE
542 static JSThread *
543 NewThread(void *id)
545 JS_ASSERT(js_CurrentThreadId() == id);
546 JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread));
547 if (!thread)
548 return NULL;
549 JS_INIT_CLIST(&thread->contextList);
550 thread->id = id;
551 if (!thread->data.init()) {
552 js_free(thread);
553 return NULL;
555 return thread;
558 static void
559 DestroyThread(JSThread *thread)
561 /* The thread must have zero contexts. */
562 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
565 * The conservative GC scanner should be disabled when the thread leaves
566 * the last request.
568 JS_ASSERT(!thread->data.conservativeGC.hasStackToScan());
570 thread->data.finish();
571 js_free(thread);
574 JSThread *
575 js_CurrentThread(JSRuntime *rt)
577 void *id = js_CurrentThreadId();
578 JS_LOCK_GC(rt);
581 * We must not race with a GC that accesses cx->thread for JSContext
582 * instances on all threads, see bug 476934.
584 js_WaitForGC(rt);
586 JSThread *thread;
587 JSThread::Map::AddPtr p = rt->threads.lookupForAdd(id);
588 if (p) {
589 thread = p->value;
592 * If thread has no contexts, it might be left over from a previous
593 * thread with the same id but a different stack address.
595 if (JS_CLIST_IS_EMPTY(&thread->contextList))
596 thread->data.nativeStackBase = GetNativeStackBase();
597 } else {
598 JS_UNLOCK_GC(rt);
599 thread = NewThread(id);
600 if (!thread)
601 return NULL;
602 JS_LOCK_GC(rt);
603 js_WaitForGC(rt);
604 if (!rt->threads.relookupOrAdd(p, id, thread)) {
605 JS_UNLOCK_GC(rt);
606 DestroyThread(thread);
607 return NULL;
610 /* Another thread cannot add an entry for the current thread id. */
611 JS_ASSERT(p->value == thread);
613 JS_ASSERT(thread->id == id);
615 #ifdef DEBUG
616 char* gnsb = (char*) GetNativeStackBase();
617 JS_ASSERT(gnsb + 0 == (char*) thread->data.nativeStackBase ||
618 /* Work around apparent glibc bug; see bug 608526. */
619 gnsb + 0x1000 == (char*) thread->data.nativeStackBase ||
620 gnsb + 0x2000 == (char*) thread->data.nativeStackBase ||
621 gnsb + 0x3000 == (char*) thread->data.nativeStackBase);
622 #endif
624 return thread;
627 JSBool
628 js_InitContextThread(JSContext *cx)
630 JSThread *thread = js_CurrentThread(cx->runtime);
631 if (!thread)
632 return false;
634 JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
635 cx->thread = thread;
636 return true;
639 void
640 js_ClearContextThread(JSContext *cx)
642 JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
643 JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
644 cx->thread = NULL;
647 #endif /* JS_THREADSAFE */
649 JSThreadData *
650 js_CurrentThreadData(JSRuntime *rt)
652 #ifdef JS_THREADSAFE
653 JSThread *thread = js_CurrentThread(rt);
654 if (!thread)
655 return NULL;
657 return &thread->data;
658 #else
659 return &rt->threadData;
660 #endif
663 JSBool
664 js_InitThreads(JSRuntime *rt)
666 #ifdef JS_THREADSAFE
667 if (!rt->threads.init(4))
668 return false;
669 #else
670 if (!rt->threadData.init())
671 return false;
672 #endif
673 return true;
676 void
677 js_FinishThreads(JSRuntime *rt)
679 #ifdef JS_THREADSAFE
680 if (!rt->threads.initialized())
681 return;
682 for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
683 JSThread *thread = r.front().value;
684 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
685 DestroyThread(thread);
687 rt->threads.clear();
688 #else
689 rt->threadData.finish();
690 #endif
693 void
694 js_PurgeThreads(JSContext *cx)
696 #ifdef JS_THREADSAFE
697 for (JSThread::Map::Enum e(cx->runtime->threads);
698 !e.empty();
699 e.popFront()) {
700 JSThread *thread = e.front().value;
702 if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
703 JS_ASSERT(cx->thread != thread);
705 DestroyThread(thread);
706 e.removeFront();
707 } else {
708 thread->data.purge(cx);
711 #else
712 cx->runtime->threadData.purge(cx);
713 #endif
716 JSContext *
717 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
719 JSContext *cx;
720 JSBool ok, first;
721 JSContextCallback cxCallback;
724 * We need to initialize the new context fully before adding it to the
725 * runtime list. After that it can be accessed from another thread via
726 * js_ContextIterator.
728 void *mem = js_calloc(sizeof *cx);
729 if (!mem)
730 return NULL;
732 cx = new (mem) JSContext(rt);
733 cx->debugHooks = &rt->globalDebugHooks;
734 #if JS_STACK_GROWTH_DIRECTION > 0
735 cx->stackLimit = (jsuword) -1;
736 #endif
737 cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
738 JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
739 JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
740 VOUCH_DOES_NOT_REQUIRE_STACK();
742 JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble),
743 &cx->scriptStackQuota);
744 JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int),
745 &cx->scriptStackQuota);
747 JS_ASSERT(cx->resolveFlags == 0);
749 #ifdef JS_THREADSAFE
750 if (!js_InitContextThread(cx)) {
751 FreeContext(cx);
752 return NULL;
754 #endif
757 * Here the GC lock is still held after js_InitContextThread took it and
758 * the GC is not running on another thread.
760 for (;;) {
761 if (rt->state == JSRTS_UP) {
762 JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
763 first = JS_FALSE;
764 break;
766 if (rt->state == JSRTS_DOWN) {
767 JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList));
768 first = JS_TRUE;
769 rt->state = JSRTS_LAUNCHING;
770 break;
772 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
775 * During the above wait after we are notified about the state change
776 * but before we wake up, another thread could enter the GC from
777 * js_DestroyContext, bug 478336. So we must wait here to ensure that
778 * when we exit the loop with the first flag set to true, that GC is
779 * finished.
781 js_WaitForGC(rt);
783 JS_APPEND_LINK(&cx->link, &rt->contextList);
784 JS_UNLOCK_GC(rt);
786 js_InitRandom(cx);
789 * If cx is the first context on this runtime, initialize well-known atoms,
790 * keywords, numbers, and strings. If one of these steps should fail, the
791 * runtime will be left in a partially initialized state, with zeroes and
792 * nulls stored in the default-initialized remainder of the struct. We'll
793 * clean the runtime up under js_DestroyContext, because cx will be "last"
794 * as well as "first".
796 if (first) {
797 #ifdef JS_THREADSAFE
798 JS_BeginRequest(cx);
799 #endif
800 ok = js_InitCommonAtoms(cx);
803 * scriptFilenameTable may be left over from a previous episode of
804 * non-zero contexts alive in rt, so don't re-init the table if it's
805 * not necessary.
807 if (ok && !rt->scriptFilenameTable)
808 ok = js_InitRuntimeScriptState(rt);
809 if (ok)
810 ok = js_InitRuntimeNumberState(cx);
811 if (ok) {
813 * Ensure that the empty scopes initialized by
814 * Shape::initRuntimeState get the desired special shapes.
815 * (The rt->state dance above guarantees that this abuse of
816 * rt->shapeGen is thread-safe.)
818 uint32 shapeGen = rt->shapeGen;
819 rt->shapeGen = 0;
820 ok = Shape::initRuntimeState(cx);
821 if (rt->shapeGen < shapeGen)
822 rt->shapeGen = shapeGen;
825 #ifdef JS_THREADSAFE
826 JS_EndRequest(cx);
827 #endif
828 if (!ok) {
829 js_DestroyContext(cx, JSDCM_NEW_FAILED);
830 return NULL;
833 AutoLockGC lock(rt);
834 rt->state = JSRTS_UP;
835 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
838 cxCallback = rt->cxCallback;
839 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
840 js_DestroyContext(cx, JSDCM_NEW_FAILED);
841 return NULL;
844 /* Using ContextAllocPolicy, so init after JSContext is ready. */
845 if (!cx->busyArrays.init()) {
846 FreeContext(cx);
847 return NULL;
850 return cx;
853 #if defined DEBUG && defined XP_UNIX
854 # include <stdio.h>
856 class JSAutoFile {
857 public:
858 JSAutoFile() : mFile(NULL) {}
860 ~JSAutoFile() {
861 if (mFile)
862 fclose(mFile);
865 FILE *open(const char *fname, const char *mode) {
866 return mFile = fopen(fname, mode);
868 operator FILE *() {
869 return mFile;
872 private:
873 FILE *mFile;
876 static void
877 DumpEvalCacheMeter(JSContext *cx)
879 if (const char *filename = getenv("JS_EVALCACHE_STATFILE")) {
880 struct {
881 const char *name;
882 ptrdiff_t offset;
883 } table[] = {
884 #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
885 EVAL_CACHE_METER_LIST(frob)
886 #undef frob
888 JSEvalCacheMeter *ecm = &cx->compartment->evalCacheMeter;
890 static JSAutoFile fp;
891 if (!fp && !fp.open(filename, "w"))
892 return;
894 fprintf(fp, "eval cache meter (%p):\n",
895 #ifdef JS_THREADSAFE
896 (void *) cx->thread
897 #else
898 (void *) cx->runtime
899 #endif
901 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
902 fprintf(fp, "%-8.8s %llu\n",
903 table[i].name,
904 (unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset));
906 fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
907 fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
908 fflush(fp);
911 # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
913 static void
914 DumpFunctionCountMap(const char *title, JSRuntime::FunctionCountMap &map, FILE *fp)
916 fprintf(fp, "\n%s count map:\n", title);
918 for (JSRuntime::FunctionCountMap::Range r = map.all(); !r.empty(); r.popFront()) {
919 JSFunction *fun = r.front().key;
920 int32 count = r.front().value;
922 fprintf(fp, "%10d %s:%u\n", count, fun->u.i.script->filename, fun->u.i.script->lineno);
926 static void
927 DumpFunctionMeter(JSContext *cx)
929 if (const char *filename = cx->runtime->functionMeterFilename) {
930 struct {
931 const char *name;
932 ptrdiff_t offset;
933 } table[] = {
934 #define frob(x) { #x, offsetof(JSFunctionMeter, x) }
935 FUNCTION_KIND_METER_LIST(frob)
936 #undef frob
938 JSFunctionMeter *fm = &cx->runtime->functionMeter;
940 static JSAutoFile fp;
941 if (!fp && !fp.open(filename, "w"))
942 return;
944 fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
945 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i)
946 fprintf(fp, "%-19.19s %d\n", table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
948 DumpFunctionCountMap("method read barrier", cx->runtime->methodReadBarrierCountMap, fp);
949 DumpFunctionCountMap("unjoined function", cx->runtime->unjoinedFunctionCountMap, fp);
951 putc('\n', fp);
952 fflush(fp);
956 # define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
958 #endif /* DEBUG && XP_UNIX */
960 #ifndef DUMP_EVAL_CACHE_METER
961 # define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
962 #endif
964 #ifndef DUMP_FUNCTION_METER
965 # define DUMP_FUNCTION_METER(cx) ((void) 0)
966 #endif
968 void
969 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
971 JSRuntime *rt;
972 JSContextCallback cxCallback;
973 JSBool last;
975 JS_ASSERT(!cx->enumerators);
977 rt = cx->runtime;
978 #ifdef JS_THREADSAFE
980 * For API compatibility we allow to destroy contexts without a thread in
981 * optimized builds. We assume that the embedding knows that an OOM error
982 * cannot happen in JS_SetContextThread.
984 JS_ASSERT(cx->thread && CURRENT_THREAD_IS_ME(cx->thread));
985 if (!cx->thread)
986 JS_SetContextThread(cx);
989 * For API compatibility we support destroying contexts with non-zero
990 * cx->outstandingRequests but we assume that all JS_BeginRequest calls
991 * on this cx contributes to cx->thread->data.requestDepth and there is no
992 * JS_SuspendRequest calls that set aside the counter.
994 JS_ASSERT(cx->outstandingRequests <= cx->thread->data.requestDepth);
995 #endif
997 if (mode != JSDCM_NEW_FAILED) {
998 cxCallback = rt->cxCallback;
999 if (cxCallback) {
1001 * JSCONTEXT_DESTROY callback is not allowed to fail and must
1002 * return true.
1004 #ifdef DEBUG
1005 JSBool callbackStatus =
1006 #endif
1007 cxCallback(cx, JSCONTEXT_DESTROY);
1008 JS_ASSERT(callbackStatus);
1012 JS_LOCK_GC(rt);
1013 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
1014 #ifdef JS_THREADSAFE
1016 * Typically we are called outside a request, so ensure that the GC is not
1017 * running before removing the context from rt->contextList, see bug 477021.
1019 if (cx->thread->data.requestDepth == 0)
1020 js_WaitForGC(rt);
1021 #endif
1022 JS_REMOVE_LINK(&cx->link);
1023 last = (rt->contextList.next == &rt->contextList);
1024 if (last)
1025 rt->state = JSRTS_LANDING;
1026 if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
1027 #ifdef JS_THREADSAFE
1028 || cx->outstandingRequests != 0
1029 #endif
1031 JS_ASSERT(!rt->gcRunning);
1033 JS_UNLOCK_GC(rt);
1035 if (last) {
1036 #ifdef JS_THREADSAFE
1038 * If this thread is not in a request already, begin one now so
1039 * that we wait for any racing GC started on a not-last context to
1040 * finish, before we plow ahead and unpin atoms. Note that even
1041 * though we begin a request here if necessary, we end all
1042 * thread's requests before forcing a final GC. This lets any
1043 * not-last context destruction racing in another thread try to
1044 * force or maybe run the GC, but by that point, rt->state will
1045 * not be JSRTS_UP, and that GC attempt will return early.
1047 if (cx->thread->data.requestDepth == 0)
1048 JS_BeginRequest(cx);
1049 #endif
1051 Shape::finishRuntimeState(cx);
1052 js_FinishRuntimeNumberState(cx);
1054 /* Unpin all common atoms before final GC. */
1055 js_FinishCommonAtoms(cx);
1057 /* Clear debugging state to remove GC roots. */
1058 JS_ClearAllTraps(cx);
1059 JS_ClearAllWatchPoints(cx);
1062 #ifdef JS_THREADSAFE
1064 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
1065 * end our request here in case we are "last" -- in that event, another
1066 * js_DestroyContext that was not last might be waiting in the GC for our
1067 * request to end. We'll let it run below, just before we do the truly
1068 * final GC and then free atom state.
1070 while (cx->outstandingRequests != 0)
1071 JS_EndRequest(cx);
1072 #endif
1074 if (last) {
1075 js_GC(cx, NULL, GC_LAST_CONTEXT);
1076 DUMP_EVAL_CACHE_METER(cx);
1077 DUMP_FUNCTION_METER(cx);
1079 /* Take the runtime down, now that it has no contexts or atoms. */
1080 JS_LOCK_GC(rt);
1081 rt->state = JSRTS_DOWN;
1082 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
1083 } else {
1084 if (mode == JSDCM_FORCE_GC)
1085 js_GC(cx, NULL, GC_NORMAL);
1086 else if (mode == JSDCM_MAYBE_GC)
1087 JS_MaybeGC(cx);
1088 JS_LOCK_GC(rt);
1089 js_WaitForGC(rt);
1092 #ifdef JS_THREADSAFE
1093 #ifdef DEBUG
1094 JSThread *t = cx->thread;
1095 #endif
1096 js_ClearContextThread(cx);
1097 JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
1098 #endif
1099 #ifdef JS_METER_DST_OFFSET_CACHING
1100 cx->dstOffsetCache.dumpStats();
1101 #endif
1102 JS_UNLOCK_GC(rt);
1103 FreeContext(cx);
1106 static void
1107 FreeContext(JSContext *cx)
1109 #ifdef JS_THREADSAFE
1110 JS_ASSERT(!cx->thread);
1111 #endif
1113 /* Free the stuff hanging off of cx. */
1114 VOUCH_DOES_NOT_REQUIRE_STACK();
1115 JS_FinishArenaPool(&cx->tempPool);
1116 JS_FinishArenaPool(&cx->regExpPool);
1118 if (cx->lastMessage)
1119 js_free(cx->lastMessage);
1121 /* Remove any argument formatters. */
1122 JSArgumentFormatMap *map = cx->argumentFormatMap;
1123 while (map) {
1124 JSArgumentFormatMap *temp = map;
1125 map = map->next;
1126 cx->free(temp);
1129 /* Destroy the resolve recursion damper. */
1130 if (cx->resolvingTable) {
1131 JS_DHashTableDestroy(cx->resolvingTable);
1132 cx->resolvingTable = NULL;
1135 /* Finally, free cx itself. */
1136 cx->~JSContext();
1137 js_free(cx);
1140 JSContext *
1141 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
1143 JSContext *cx = *iterp;
1145 Conditionally<AutoLockGC> lockIf(!!unlocked, rt);
1146 cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next);
1147 if (&cx->link == &rt->contextList)
1148 cx = NULL;
1149 *iterp = cx;
1150 return cx;
1153 JS_FRIEND_API(JSContext *)
1154 js_NextActiveContext(JSRuntime *rt, JSContext *cx)
1156 JSContext *iter = cx;
1157 #ifdef JS_THREADSAFE
1158 while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
1159 if (cx->outstandingRequests && cx->thread->data.requestDepth)
1160 break;
1162 return cx;
1163 #else
1164 return js_ContextIterator(rt, JS_FALSE, &iter);
1165 #endif
1168 static JSDHashNumber
1169 resolving_HashKey(JSDHashTable *table, const void *ptr)
1171 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
1173 return (JSDHashNumber(uintptr_t(key->obj)) >> JS_GCTHING_ALIGN) ^ JSID_BITS(key->id);
1176 static JSBool
1177 resolving_MatchEntry(JSDHashTable *table,
1178 const JSDHashEntryHdr *hdr,
1179 const void *ptr)
1181 const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
1182 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
1184 return entry->key.obj == key->obj && entry->key.id == key->id;
1187 static const JSDHashTableOps resolving_dhash_ops = {
1188 JS_DHashAllocTable,
1189 JS_DHashFreeTable,
1190 resolving_HashKey,
1191 resolving_MatchEntry,
1192 JS_DHashMoveEntryStub,
1193 JS_DHashClearEntryStub,
1194 JS_DHashFinalizeStub,
1195 NULL
1198 JSBool
1199 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
1200 JSResolvingEntry **entryp)
1202 JSDHashTable *table;
1203 JSResolvingEntry *entry;
1205 table = cx->resolvingTable;
1206 if (!table) {
1207 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
1208 sizeof(JSResolvingEntry),
1209 JS_DHASH_MIN_SIZE);
1210 if (!table)
1211 goto outofmem;
1212 cx->resolvingTable = table;
1215 entry = (JSResolvingEntry *)
1216 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
1217 if (!entry)
1218 goto outofmem;
1220 if (entry->flags & flag) {
1221 /* An entry for (key, flag) exists already -- dampen recursion. */
1222 entry = NULL;
1223 } else {
1224 /* Fill in key if we were the first to add entry, then set flag. */
1225 if (!entry->key.obj)
1226 entry->key = *key;
1227 entry->flags |= flag;
1229 *entryp = entry;
1230 return JS_TRUE;
1232 outofmem:
1233 JS_ReportOutOfMemory(cx);
1234 return JS_FALSE;
1237 void
1238 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
1239 JSResolvingEntry *entry, uint32 generation)
1241 JSDHashTable *table;
1244 * Clear flag from entry->flags and return early if other flags remain.
1245 * We must take care to re-lookup entry if the table has changed since
1246 * it was found by js_StartResolving.
1248 table = cx->resolvingTable;
1249 if (!entry || table->generation != generation) {
1250 entry = (JSResolvingEntry *)
1251 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
1253 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
1254 entry->flags &= ~flag;
1255 if (entry->flags)
1256 return;
1259 * Do a raw remove only if fewer entries were removed than would cause
1260 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
1261 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
1262 * compressing or shrinking the table as needed.
1264 if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
1265 JS_DHashTableRawRemove(table, &entry->hdr);
1266 else
1267 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
1270 static void
1271 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
1272 JSErrorCallback callback, void *userRef)
1275 * Check the error report, and set a JavaScript-catchable exception
1276 * if the error is defined to have an associated exception. If an
1277 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
1278 * on the error report, and exception-aware hosts should ignore it.
1280 JS_ASSERT(reportp);
1281 if ((!callback || callback == js_GetErrorMessage) &&
1282 reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
1283 reportp->flags |= JSREPORT_EXCEPTION;
1286 * Call the error reporter only if an exception wasn't raised.
1288 * If an exception was raised, then we call the debugErrorHook
1289 * (if present) to give it a chance to see the error before it
1290 * propagates out of scope. This is needed for compatability
1291 * with the old scheme.
1293 if (!JS_IsRunning(cx) ||
1294 !js_ErrorToException(cx, message, reportp, callback, userRef)) {
1295 js_ReportErrorAgain(cx, message, reportp);
1296 } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
1297 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1298 /* test local in case debugErrorHook changed on another thread */
1299 if (hook)
1300 hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
1304 /* The report must be initially zeroed. */
1305 static void
1306 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
1309 * Walk stack until we find a frame that is associated with some script
1310 * rather than a native frame.
1312 for (JSStackFrame *fp = js_GetTopStackFrame(cx); fp; fp = fp->prev()) {
1313 if (fp->pc(cx)) {
1314 report->filename = fp->script()->filename;
1315 report->lineno = js_FramePCToLineNumber(cx, fp);
1316 break;
1322 * We don't post an exception in this case, since doing so runs into
1323 * complications of pre-allocating an exception object which required
1324 * running the Exception class initializer early etc.
1325 * Instead we just invoke the errorReporter with an "Out Of Memory"
1326 * type message, and then hope the process ends swiftly.
1328 void
1329 js_ReportOutOfMemory(JSContext *cx)
1331 #ifdef JS_TRACER
1333 * If we are in a builtin called directly from trace, don't report an
1334 * error. We will retry in the interpreter instead.
1336 if (JS_ON_TRACE(cx) && !cx->bailExit)
1337 return;
1338 #endif
1340 JSErrorReport report;
1341 JSErrorReporter onError = cx->errorReporter;
1343 /* Get the message for this error, but we won't expand any arguments. */
1344 const JSErrorFormatString *efs =
1345 js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
1346 const char *msg = efs ? efs->format : "Out of memory";
1348 /* Fill out the report, but don't do anything that requires allocation. */
1349 PodZero(&report);
1350 report.flags = JSREPORT_ERROR;
1351 report.errorNumber = JSMSG_OUT_OF_MEMORY;
1352 PopulateReportBlame(cx, &report);
1355 * If debugErrorHook is present then we give it a chance to veto sending
1356 * the error on to the regular ErrorReporter. We also clear a pending
1357 * exception if any now so the hooks can replace the out-of-memory error
1358 * by a script-catchable exception.
1360 cx->clearPendingException();
1361 if (onError) {
1362 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1363 if (hook &&
1364 !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
1365 onError = NULL;
1369 if (onError)
1370 onError(cx, msg, &report);
1373 void
1374 js_ReportOutOfScriptQuota(JSContext *cx)
1376 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1377 JSMSG_SCRIPT_STACK_QUOTA);
1380 JS_FRIEND_API(void)
1381 js_ReportOverRecursed(JSContext *cx)
1383 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1386 void
1387 js_ReportAllocationOverflow(JSContext *cx)
1389 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
1393 * Given flags and the state of cx, decide whether we should report an
1394 * error, a warning, or just continue execution normally. Return
1395 * true if we should continue normally, without reporting anything;
1396 * otherwise, adjust *flags as appropriate and return false.
1398 static bool
1399 checkReportFlags(JSContext *cx, uintN *flags)
1401 if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
1403 * Error in strict code; warning with strict option; okay otherwise.
1404 * We assume that if the top frame is a native, then it is strict if
1405 * the nearest scripted frame is strict, see bug 536306.
1407 JSStackFrame *fp = js_GetScriptedCaller(cx, NULL);
1408 if (fp && fp->script()->strictModeCode)
1409 *flags &= ~JSREPORT_WARNING;
1410 else if (JS_HAS_STRICT_OPTION(cx))
1411 *flags |= JSREPORT_WARNING;
1412 else
1413 return true;
1414 } else if (JSREPORT_IS_STRICT(*flags)) {
1415 /* Warning/error only when JSOPTION_STRICT is set. */
1416 if (!JS_HAS_STRICT_OPTION(cx))
1417 return true;
1420 /* Warnings become errors when JSOPTION_WERROR is set. */
1421 if (JSREPORT_IS_WARNING(*flags) && JS_HAS_WERROR_OPTION(cx))
1422 *flags &= ~JSREPORT_WARNING;
1424 return false;
1427 JSBool
1428 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
1430 char *message;
1431 jschar *ucmessage;
1432 size_t messagelen;
1433 JSErrorReport report;
1434 JSBool warning;
1436 if (checkReportFlags(cx, &flags))
1437 return JS_TRUE;
1439 message = JS_vsmprintf(format, ap);
1440 if (!message)
1441 return JS_FALSE;
1442 messagelen = strlen(message);
1444 PodZero(&report);
1445 report.flags = flags;
1446 report.errorNumber = JSMSG_USER_DEFINED_ERROR;
1447 report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
1448 PopulateReportBlame(cx, &report);
1450 warning = JSREPORT_IS_WARNING(report.flags);
1452 ReportError(cx, message, &report, NULL, NULL);
1453 js_free(message);
1454 cx->free(ucmessage);
1455 return warning;
1459 * The arguments from ap need to be packaged up into an array and stored
1460 * into the report struct.
1462 * The format string addressed by the error number may contain operands
1463 * identified by the format {N}, where N is a decimal digit. Each of these
1464 * is to be replaced by the Nth argument from the va_list. The complete
1465 * message is placed into reportp->ucmessage converted to a JSString.
1467 * Returns true if the expansion succeeds (can fail if out of memory).
1469 JSBool
1470 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
1471 void *userRef, const uintN errorNumber,
1472 char **messagep, JSErrorReport *reportp,
1473 bool charArgs, va_list ap)
1475 const JSErrorFormatString *efs;
1476 int i;
1477 int argCount;
1479 *messagep = NULL;
1481 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1482 if (!callback || callback == js_GetErrorMessage)
1483 efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
1484 else
1485 efs = callback(userRef, NULL, errorNumber);
1486 if (efs) {
1487 size_t totalArgsLength = 0;
1488 size_t argLengths[10]; /* only {0} thru {9} supported */
1489 argCount = efs->argCount;
1490 JS_ASSERT(argCount <= 10);
1491 if (argCount > 0) {
1493 * Gather the arguments into an array, and accumulate
1494 * their sizes. We allocate 1 more than necessary and
1495 * null it out to act as the caboose when we free the
1496 * pointers later.
1498 reportp->messageArgs = (const jschar **)
1499 cx->malloc(sizeof(jschar *) * (argCount + 1));
1500 if (!reportp->messageArgs)
1501 return JS_FALSE;
1502 reportp->messageArgs[argCount] = NULL;
1503 for (i = 0; i < argCount; i++) {
1504 if (charArgs) {
1505 char *charArg = va_arg(ap, char *);
1506 size_t charArgLength = strlen(charArg);
1507 reportp->messageArgs[i]
1508 = js_InflateString(cx, charArg, &charArgLength);
1509 if (!reportp->messageArgs[i])
1510 goto error;
1511 } else {
1512 reportp->messageArgs[i] = va_arg(ap, jschar *);
1514 argLengths[i] = js_strlen(reportp->messageArgs[i]);
1515 totalArgsLength += argLengths[i];
1517 /* NULL-terminate for easy copying. */
1518 reportp->messageArgs[i] = NULL;
1521 * Parse the error format, substituting the argument X
1522 * for {X} in the format.
1524 if (argCount > 0) {
1525 if (efs->format) {
1526 jschar *buffer, *fmt, *out;
1527 int expandedArgs = 0;
1528 size_t expandedLength;
1529 size_t len = strlen(efs->format);
1531 buffer = fmt = js_InflateString (cx, efs->format, &len);
1532 if (!buffer)
1533 goto error;
1534 expandedLength = len
1535 - (3 * argCount) /* exclude the {n} */
1536 + totalArgsLength;
1539 * Note - the above calculation assumes that each argument
1540 * is used once and only once in the expansion !!!
1542 reportp->ucmessage = out = (jschar *)
1543 cx->malloc((expandedLength + 1) * sizeof(jschar));
1544 if (!out) {
1545 cx->free(buffer);
1546 goto error;
1548 while (*fmt) {
1549 if (*fmt == '{') {
1550 if (isdigit(fmt[1])) {
1551 int d = JS7_UNDEC(fmt[1]);
1552 JS_ASSERT(d < argCount);
1553 js_strncpy(out, reportp->messageArgs[d],
1554 argLengths[d]);
1555 out += argLengths[d];
1556 fmt += 3;
1557 expandedArgs++;
1558 continue;
1561 *out++ = *fmt++;
1563 JS_ASSERT(expandedArgs == argCount);
1564 *out = 0;
1565 cx->free(buffer);
1566 *messagep =
1567 js_DeflateString(cx, reportp->ucmessage,
1568 (size_t)(out - reportp->ucmessage));
1569 if (!*messagep)
1570 goto error;
1572 } else {
1574 * Zero arguments: the format string (if it exists) is the
1575 * entire message.
1577 if (efs->format) {
1578 size_t len;
1579 *messagep = JS_strdup(cx, efs->format);
1580 if (!*messagep)
1581 goto error;
1582 len = strlen(*messagep);
1583 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1584 if (!reportp->ucmessage)
1585 goto error;
1589 if (*messagep == NULL) {
1590 /* where's the right place for this ??? */
1591 const char *defaultErrorMessage
1592 = "No error message available for error number %d";
1593 size_t nbytes = strlen(defaultErrorMessage) + 16;
1594 *messagep = (char *)cx->malloc(nbytes);
1595 if (!*messagep)
1596 goto error;
1597 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1599 return JS_TRUE;
1601 error:
1602 if (reportp->messageArgs) {
1603 /* free the arguments only if we allocated them */
1604 if (charArgs) {
1605 i = 0;
1606 while (reportp->messageArgs[i])
1607 cx->free((void *)reportp->messageArgs[i++]);
1609 cx->free((void *)reportp->messageArgs);
1610 reportp->messageArgs = NULL;
1612 if (reportp->ucmessage) {
1613 cx->free((void *)reportp->ucmessage);
1614 reportp->ucmessage = NULL;
1616 if (*messagep) {
1617 cx->free((void *)*messagep);
1618 *messagep = NULL;
1620 return JS_FALSE;
1623 JSBool
1624 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1625 void *userRef, const uintN errorNumber,
1626 JSBool charArgs, va_list ap)
1628 JSErrorReport report;
1629 char *message;
1630 JSBool warning;
1632 if (checkReportFlags(cx, &flags))
1633 return JS_TRUE;
1634 warning = JSREPORT_IS_WARNING(flags);
1636 PodZero(&report);
1637 report.flags = flags;
1638 report.errorNumber = errorNumber;
1639 PopulateReportBlame(cx, &report);
1641 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1642 &message, &report, !!charArgs, ap)) {
1643 return JS_FALSE;
1646 ReportError(cx, message, &report, callback, userRef);
1648 if (message)
1649 cx->free(message);
1650 if (report.messageArgs) {
1652 * js_ExpandErrorArguments owns its messageArgs only if it had to
1653 * inflate the arguments (from regular |char *|s).
1655 if (charArgs) {
1656 int i = 0;
1657 while (report.messageArgs[i])
1658 cx->free((void *)report.messageArgs[i++]);
1660 cx->free((void *)report.messageArgs);
1662 if (report.ucmessage)
1663 cx->free((void *)report.ucmessage);
1665 return warning;
1668 JS_FRIEND_API(void)
1669 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1671 JSErrorReporter onError;
1673 if (!message)
1674 return;
1676 if (cx->lastMessage)
1677 js_free(cx->lastMessage);
1678 cx->lastMessage = JS_strdup(cx, message);
1679 if (!cx->lastMessage)
1680 return;
1681 onError = cx->errorReporter;
1684 * If debugErrorHook is present then we give it a chance to veto
1685 * sending the error on to the regular ErrorReporter.
1687 if (onError) {
1688 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1689 if (hook &&
1690 !hook(cx, cx->lastMessage, reportp,
1691 cx->debugHooks->debugErrorHookData)) {
1692 onError = NULL;
1695 if (onError)
1696 onError(cx, cx->lastMessage, reportp);
1699 void
1700 js_ReportIsNotDefined(JSContext *cx, const char *name)
1702 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1705 JSBool
1706 js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, const Value &v,
1707 JSString *fallback)
1709 char *bytes;
1710 JSBool ok;
1712 bytes = DecompileValueGenerator(cx, spindex, v, fallback);
1713 if (!bytes)
1714 return JS_FALSE;
1716 if (strcmp(bytes, js_undefined_str) == 0 ||
1717 strcmp(bytes, js_null_str) == 0) {
1718 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1719 js_GetErrorMessage, NULL,
1720 JSMSG_NO_PROPERTIES, bytes,
1721 NULL, NULL);
1722 } else if (v.isUndefined()) {
1723 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1724 js_GetErrorMessage, NULL,
1725 JSMSG_UNEXPECTED_TYPE, bytes,
1726 js_undefined_str, NULL);
1727 } else {
1728 JS_ASSERT(v.isNull());
1729 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1730 js_GetErrorMessage, NULL,
1731 JSMSG_UNEXPECTED_TYPE, bytes,
1732 js_null_str, NULL);
1735 cx->free(bytes);
1736 return ok;
1739 void
1740 js_ReportMissingArg(JSContext *cx, const Value &v, uintN arg)
1742 char argbuf[11];
1743 char *bytes;
1744 JSAtom *atom;
1746 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1747 bytes = NULL;
1748 if (IsFunctionObject(v)) {
1749 atom = GET_FUNCTION_PRIVATE(cx, &v.toObject())->atom;
1750 bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
1751 v, ATOM_TO_STRING(atom));
1752 if (!bytes)
1753 return;
1755 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1756 JSMSG_MISSING_FUN_ARG, argbuf,
1757 bytes ? bytes : "");
1758 cx->free(bytes);
1761 JSBool
1762 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1763 intN spindex, const Value &v, JSString *fallback,
1764 const char *arg1, const char *arg2)
1766 char *bytes;
1767 JSBool ok;
1769 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1770 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1771 bytes = DecompileValueGenerator(cx, spindex, v, fallback);
1772 if (!bytes)
1773 return JS_FALSE;
1775 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1776 NULL, errorNumber, bytes, arg1, arg2);
1777 cx->free(bytes);
1778 return ok;
1781 #if defined DEBUG && defined XP_UNIX
1782 /* For gdb usage. */
1783 void js_logon(JSContext *cx) { cx->logfp = stderr; cx->logPrevPc = NULL; }
1784 void js_logoff(JSContext *cx) { cx->logfp = NULL; }
1785 #endif
1787 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1788 #define MSG_DEF(name, number, count, exception, format) \
1789 { format, count, exception } ,
1790 #include "js.msg"
1791 #undef MSG_DEF
1794 JS_FRIEND_API(const JSErrorFormatString *)
1795 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1797 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1798 return &js_ErrorFormatString[errorNumber];
1799 return NULL;
1802 JSBool
1803 js_InvokeOperationCallback(JSContext *cx)
1805 JSRuntime *rt = cx->runtime;
1806 JSThreadData *td = JS_THREAD_DATA(cx);
1808 JS_ASSERT_REQUEST_DEPTH(cx);
1809 JS_ASSERT(td->interruptFlags != 0);
1812 * Reset the callback counter first, then run GC and yield. If another
1813 * thread is racing us here we will accumulate another callback request
1814 * which will be serviced at the next opportunity.
1816 JS_LOCK_GC(rt);
1817 td->interruptFlags = 0;
1818 #ifdef JS_THREADSAFE
1819 JS_ATOMIC_DECREMENT(&rt->interruptCounter);
1820 #endif
1821 JS_UNLOCK_GC(rt);
1823 if (rt->gcIsNeeded) {
1824 js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL);
1827 * On trace we can exceed the GC quota, see comments in NewGCArena. So
1828 * we check the quota and report OOM here when we are off trace.
1830 bool delayedOutOfMemory;
1831 JS_LOCK_GC(rt);
1832 delayedOutOfMemory = (rt->gcBytes > rt->gcMaxBytes);
1833 JS_UNLOCK_GC(rt);
1834 if (delayedOutOfMemory) {
1835 js_ReportOutOfMemory(cx);
1836 return false;
1840 #ifdef JS_THREADSAFE
1842 * We automatically yield the current context every time the operation
1843 * callback is hit since we might be called as a result of an impending
1844 * GC on another thread, which would deadlock if we do not yield.
1845 * Operation callbacks are supposed to happen rarely (seconds, not
1846 * milliseconds) so it is acceptable to yield at every callback.
1848 * As the GC can be canceled before it does any request checks we yield
1849 * even if rt->gcIsNeeded was true above. See bug 590533.
1851 JS_YieldRequest(cx);
1852 #endif
1854 JSOperationCallback cb = cx->operationCallback;
1857 * Important: Additional callbacks can occur inside the callback handler
1858 * if it re-enters the JS engine. The embedding must ensure that the
1859 * callback is disconnected before attempting such re-entry.
1862 return !cb || cb(cx);
1865 JSBool
1866 js_HandleExecutionInterrupt(JSContext *cx)
1868 JSBool result = JS_TRUE;
1869 if (JS_THREAD_DATA(cx)->interruptFlags)
1870 result = js_InvokeOperationCallback(cx) && result;
1871 return result;
1874 namespace js {
1876 void
1877 TriggerOperationCallback(JSContext *cx)
1880 * We allow for cx to come from another thread. Thus we must deal with
1881 * possible JS_ClearContextThread calls when accessing cx->thread. But we
1882 * assume that the calling thread is in a request so JSThread cannot be
1883 * GC-ed.
1885 JSThreadData *td;
1886 #ifdef JS_THREADSAFE
1887 JSThread *thread = cx->thread;
1888 if (!thread)
1889 return;
1890 td = &thread->data;
1891 #else
1892 td = JS_THREAD_DATA(cx);
1893 #endif
1894 td->triggerOperationCallback(cx->runtime);
1897 void
1898 TriggerAllOperationCallbacks(JSRuntime *rt)
1900 for (ThreadDataIter i(rt); !i.empty(); i.popFront())
1901 i.threadData()->triggerOperationCallback(rt);
1904 } /* namespace js */
1906 JSStackFrame *
1907 js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1909 if (!fp)
1910 fp = js_GetTopStackFrame(cx);
1911 while (fp && fp->isDummyFrame())
1912 fp = fp->prev();
1913 JS_ASSERT_IF(fp, fp->isScriptFrame());
1914 return fp;
1917 jsbytecode*
1918 js_GetCurrentBytecodePC(JSContext* cx)
1920 jsbytecode *pc, *imacpc;
1922 #ifdef JS_TRACER
1923 if (JS_ON_TRACE(cx)) {
1924 pc = cx->bailExit->pc;
1925 imacpc = cx->bailExit->imacpc;
1926 } else
1927 #endif
1929 JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */
1930 pc = cx->regs ? cx->regs->pc : NULL;
1931 if (!pc)
1932 return NULL;
1933 imacpc = cx->fp()->maybeImacropc();
1937 * If we are inside GetProperty_tn or similar, return a pointer to the
1938 * current instruction in the script, not the CALL instruction in the
1939 * imacro, for the benefit of callers doing bytecode inspection.
1941 return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;
1944 bool
1945 js_CurrentPCIsInImacro(JSContext *cx)
1947 #ifdef JS_TRACER
1948 VOUCH_DOES_NOT_REQUIRE_STACK();
1949 if (JS_ON_TRACE(cx))
1950 return cx->bailExit->imacpc != NULL;
1951 return cx->fp()->hasImacropc();
1952 #else
1953 return false;
1954 #endif
1957 void
1958 DSTOffsetCache::purge()
1961 * NB: The initial range values are carefully chosen to result in a cache
1962 * miss on first use given the range of possible values. Be careful
1963 * to keep these values and the caching algorithm in sync!
1965 offsetMilliseconds = 0;
1966 rangeStartSeconds = rangeEndSeconds = INT64_MIN;
1967 oldOffsetMilliseconds = 0;
1968 oldRangeStartSeconds = oldRangeEndSeconds = INT64_MIN;
1970 #ifdef JS_METER_DST_OFFSET_CACHING
1971 totalCalculations = 0;
1972 hit = 0;
1973 missIncreasing = missDecreasing = 0;
1974 missIncreasingOffsetChangeExpand = missIncreasingOffsetChangeUpper = 0;
1975 missDecreasingOffsetChangeExpand = missDecreasingOffsetChangeLower = 0;
1976 missLargeIncrease = missLargeDecrease = 0;
1977 #endif
1979 sanityCheck();
1983 * Since getDSTOffsetMilliseconds guarantees that all times seen will be
1984 * positive, we can initialize the range at construction time with large
1985 * negative numbers to ensure the first computation is always a cache miss and
1986 * doesn't return a bogus offset.
1988 DSTOffsetCache::DSTOffsetCache()
1990 purge();
1993 JSContext::JSContext(JSRuntime *rt)
1994 : runtime(rt),
1995 compartment(rt->defaultCompartment),
1996 regs(NULL),
1997 busyArrays(thisInInitializer())
2000 void
2001 JSContext::resetCompartment()
2003 JSObject *scopeobj;
2004 if (hasfp()) {
2005 scopeobj = &fp()->scopeChain();
2006 } else {
2007 scopeobj = globalObject;
2008 if (!scopeobj)
2009 goto error;
2012 * Innerize. Assert, but check anyway, that this succeeds. (It
2013 * can only fail due to bugs in the engine or embedding.)
2015 OBJ_TO_INNER_OBJECT(this, scopeobj);
2016 if (!scopeobj)
2017 goto error;
2020 compartment = scopeobj->compartment();
2022 if (isExceptionPending())
2023 wrapPendingException();
2024 return;
2026 error:
2029 * If we try to use the context without a selected compartment,
2030 * we will crash.
2032 compartment = NULL;
2036 * Since this function is only called in the context of a pending exception,
2037 * the caller must subsequently take an error path. If wrapping fails, we leave
2038 * the exception cleared, which, in the context of an error path, will be
2039 * interpreted as an uncatchable exception.
2041 void
2042 JSContext::wrapPendingException()
2044 Value v = getPendingException();
2045 clearPendingException();
2046 if (compartment->wrap(this, &v))
2047 setPendingException(v);
2050 void
2051 JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
2053 JS_ASSERT(regs != &newregs);
2054 if (hasActiveSegment())
2055 currentSegment->suspend(regs);
2056 newseg->setPreviousInContext(currentSegment);
2057 currentSegment = newseg;
2058 setCurrentRegs(&newregs);
2059 newseg->joinContext(this, newregs.fp);
2062 void
2063 JSContext::popSegmentAndFrame()
2065 JS_ASSERT(currentSegment->maybeContext() == this);
2066 JS_ASSERT(currentSegment->getInitialFrame() == regs->fp);
2067 currentSegment->leaveContext();
2068 currentSegment = currentSegment->getPreviousInContext();
2069 if (currentSegment) {
2070 if (currentSegment->isSaved()) {
2071 setCurrentRegs(NULL);
2072 } else {
2073 setCurrentRegs(currentSegment->getSuspendedRegs());
2074 currentSegment->resume();
2076 } else {
2077 JS_ASSERT(regs->fp->prev() == NULL);
2078 setCurrentRegs(NULL);
2082 void
2083 JSContext::saveActiveSegment()
2085 JS_ASSERT(hasActiveSegment());
2086 currentSegment->save(regs);
2087 setCurrentRegs(NULL);
2090 void
2091 JSContext::restoreSegment()
2093 js::StackSegment *ccs = currentSegment;
2094 setCurrentRegs(ccs->getSuspendedRegs());
2095 ccs->restore();
2098 JSGenerator *
2099 JSContext::generatorFor(JSStackFrame *fp) const
2101 JS_ASSERT(stack().contains(fp) && fp->isGeneratorFrame());
2102 JS_ASSERT(!fp->isFloatingGenerator());
2103 JS_ASSERT(!genStack.empty());
2105 if (JS_LIKELY(fp == genStack.back()->liveFrame()))
2106 return genStack.back();
2108 /* General case; should only be needed for debug APIs. */
2109 for (size_t i = 0; i < genStack.length(); ++i) {
2110 if (genStack[i]->liveFrame() == fp)
2111 return genStack[i];
2113 JS_NOT_REACHED("no matching generator");
2114 return NULL;
2117 StackSegment *
2118 JSContext::containingSegment(const JSStackFrame *target)
2120 /* The context may have nothing running. */
2121 StackSegment *seg = currentSegment;
2122 if (!seg)
2123 return NULL;
2125 /* The active segments's top frame is cx->regs->fp. */
2126 if (regs) {
2127 JS_ASSERT(regs->fp);
2128 JS_ASSERT(activeSegment() == seg);
2129 JSStackFrame *f = regs->fp;
2130 JSStackFrame *stop = seg->getInitialFrame()->prev();
2131 for (; f != stop; f = f->prev()) {
2132 if (f == target)
2133 return seg;
2135 seg = seg->getPreviousInContext();
2138 /* A suspended segment's top frame is its suspended frame. */
2139 for (; seg; seg = seg->getPreviousInContext()) {
2140 JSStackFrame *f = seg->getSuspendedFrame();
2141 JSStackFrame *stop = seg->getInitialFrame()->prev();
2142 for (; f != stop; f = f->prev()) {
2143 if (f == target)
2144 return seg;
2148 return NULL;
2151 JS_FRIEND_API(void)
2152 JSRuntime::onTooMuchMalloc()
2154 #ifdef JS_THREADSAFE
2155 AutoLockGC lock(this);
2158 * We can be called outside a request and can race against a GC that
2159 * mutates the JSThread set during the sweeping phase.
2161 js_WaitForGC(this);
2162 #endif
2163 TriggerGC(this);
2166 JS_FRIEND_API(void *)
2167 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
2169 #ifdef JS_THREADSAFE
2170 gcHelperThread.waitBackgroundSweepEnd(this);
2171 if (!p)
2172 p = ::js_malloc(nbytes);
2173 else if (p == reinterpret_cast<void *>(1))
2174 p = ::js_calloc(nbytes);
2175 else
2176 p = ::js_realloc(p, nbytes);
2177 if (p)
2178 return p;
2179 #endif
2180 if (cx)
2181 js_ReportOutOfMemory(cx);
2182 return NULL;
2186 * Release pool's arenas if the stackPool has existed for longer than the
2187 * limit specified by gcEmptyArenaPoolLifespan.
2189 inline void
2190 FreeOldArenas(JSRuntime *rt, JSArenaPool *pool)
2192 JSArena *a = pool->current;
2193 if (a == pool->first.next && a->avail == a->base + sizeof(int64)) {
2194 int64 age = JS_Now() - *(int64 *) a->base;
2195 if (age > int64(rt->gcEmptyArenaPoolLifespan) * 1000)
2196 JS_FreeArenaPool(pool);
2200 void
2201 JSContext::purge()
2203 FreeOldArenas(runtime, &regExpPool);
2206 static bool
2207 ComputeIsJITBroken()
2209 #ifndef ANDROID
2210 return false;
2211 #else // ANDROID
2212 if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
2213 return false;
2216 std::string line;
2218 // Check for the known-bad kernel version (2.6.29).
2219 std::ifstream osrelease("/proc/sys/kernel/osrelease");
2220 std::getline(osrelease, line);
2221 __android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'",
2222 line.c_str());
2224 if (line.npos == line.find("2.6.29")) {
2225 // We're using something other than 2.6.29, so the JITs should work.
2226 __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken");
2227 return false;
2230 // We're using 2.6.29, and this causes trouble with the JITs on i9000.
2231 line = "";
2232 bool broken = false;
2233 std::ifstream cpuinfo("/proc/cpuinfo");
2234 do {
2235 if (0 == line.find("Hardware")) {
2236 const char* blacklist[] = {
2237 "SGH-T959", // Samsung i9000, Vibrant device
2238 "SGH-I897", // Samsung i9000, Captivate device
2239 "SCH-I500", // Samsung i9000, Fascinate device
2240 "SPH-D700", // Samsung i9000, Epic device
2241 "GT-I9000", // Samsung i9000, UK/Europe device
2242 NULL
2244 for (const char** hw = &blacklist[0]; *hw; ++hw) {
2245 if (line.npos != line.find(*hw)) {
2246 __android_log_print(ANDROID_LOG_INFO, "Gecko",
2247 "Blacklisted device `%s'", *hw);
2248 broken = true;
2249 break;
2252 break;
2254 std::getline(cpuinfo, line);
2255 } while(!cpuinfo.fail() && !cpuinfo.eof());
2257 __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken",
2258 broken ? "" : "not ");
2260 return broken;
2261 #endif // ifndef ANDROID
2264 static bool
2265 IsJITBrokenHere()
2267 static bool computedIsBroken = false;
2268 static bool isBroken = false;
2269 if (!computedIsBroken) {
2270 isBroken = ComputeIsJITBroken();
2271 computedIsBroken = true;
2273 return isBroken;
2276 void
2277 JSContext::updateJITEnabled()
2279 #ifdef JS_TRACER
2280 traceJitEnabled = ((options & JSOPTION_JIT) &&
2281 !IsJITBrokenHere() &&
2282 (debugHooks == &js_NullDebugHooks ||
2283 (debugHooks == &runtime->globalDebugHooks &&
2284 !runtime->debuggerInhibitsJIT())));
2285 #endif
2286 #ifdef JS_METHODJIT
2287 methodJitEnabled = (options & JSOPTION_METHODJIT) &&
2288 !IsJITBrokenHere()
2289 # if defined JS_CPU_X86 || defined JS_CPU_X64
2290 && JSC::MacroAssemblerX86Common::getSSEState() >=
2291 JSC::MacroAssemblerX86Common::HasSSE2
2292 # endif
2294 #ifdef JS_TRACER
2295 profilingEnabled = (options & JSOPTION_PROFILING) && traceJitEnabled && methodJitEnabled;
2296 #endif
2297 #endif
2300 namespace js {
2302 JS_FORCES_STACK JS_FRIEND_API(void)
2303 LeaveTrace(JSContext *cx)
2305 #ifdef JS_TRACER
2306 if (JS_ON_TRACE(cx))
2307 DeepBail(cx);
2308 #endif
2311 } /* namespace js */