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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
49 # include <android/log.h>
63 #include "jsversion.h"
71 #include "jsnativestack.h"
79 #include "jsstaticcheck.h"
84 # include "assembler/assembler/MacroAssembler.h"
87 #include "jscntxtinlines.h"
88 #include "jscompartment.h"
89 #include "jsinterpinlines.h"
90 #include "jsobjinlines.h"
95 # define INCL_DOSMEMMGR
99 # include <sys/mman.h>
100 # if !defined(MAP_ANONYMOUS)
101 # if defined(MAP_ANON)
102 # define MAP_ANONYMOUS MAP_ANON
104 # define MAP_ANONYMOUS 0
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
;
116 FreeContext(JSContext
*cx
);
119 JS_REQUIRES_STACK
bool
120 StackSegment::contains(const JSStackFrame
*fp
) const
122 JS_ASSERT(inContext());
126 JS_ASSERT(cx
->hasfp());
128 stop
= cx
->activeSegment()->initialFrame
->prev();
130 JS_ASSERT(suspendedRegs
&& suspendedRegs
->fp
);
131 start
= suspendedRegs
->fp
;
132 stop
= initialFrame
->prev();
134 for (JSStackFrame
*f
= start
; f
!= stop
; f
= f
->prev()) {
147 p
= VirtualAlloc(NULL
, CAPACITY_BYTES
, MEM_RESERVE
, PAGE_READWRITE
);
150 void *check
= VirtualAlloc(p
, COMMIT_BYTES
, MEM_COMMIT
, PAGE_READWRITE
);
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
))
160 base
= reinterpret_cast<Value
*>(p
);
161 end
= base
+ CAPACITY_VALS
;
163 JS_ASSERT(CAPACITY_BYTES
% getpagesize() == 0);
164 p
= mmap(NULL
, CAPACITY_BYTES
, PROT_READ
| PROT_WRITE
, MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
167 base
= reinterpret_cast<Value
*>(p
);
168 end
= base
+ CAPACITY_VALS
;
177 VirtualFree(base
, (commitEnd
- base
) * sizeof(Value
), MEM_DECOMMIT
);
178 VirtualFree(base
, 0, MEM_RELEASE
);
179 #elif defined(XP_OS2)
183 munmap((caddr_t
)base
, CAPACITY_BYTES
);
185 munmap(base
, CAPACITY_BYTES
);
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);
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
))
210 commitEnd
= newCommit
;
216 StackSpace::mark(JSTracer
*trc
)
219 * The correctness/completeness of marking depends on the continuity
220 * invariants described by the StackSegment and StackSpace definitions.
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
);
250 /* Mark slots/args trailing off segment. */
251 MarkValueRange(trc
, seg
->valueRangeBegin(), end
, "stack");
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
))
265 StackSegment
*seg
= new(start
) StackSegment
;
266 seg
->setPreviousInMemory(currentSegment
);
267 currentSegment
= seg
;
271 ag
->argv_
= seg
->valueRangeBegin() + 2;
274 /* Use invokeArgEnd to root [vp, vpend) until the frame is pushed. */
276 ag
->prevInvokeSegment
= invokeSegment
;
278 ag
->prevInvokeFrame
= invokeFrame
;
281 ag
->prevInvokeArgEnd
= invokeArgEnd
;
282 invokeArgEnd
= ag
->argv() + ag
->argc();
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();
297 invokeSegment
= ag
.prevInvokeSegment
;
298 invokeFrame
= ag
.prevInvokeFrame
;
300 invokeArgEnd
= ag
.prevInvokeArgEnd
;
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
))
312 fg
->seg_
= new(start
) StackSegment
;
313 fg
->vp_
= start
+ VALUES_PER_STACK_SEGMENT
;
314 fg
->fp_
= reinterpret_cast<JSStackFrame
*>(fg
->vp() + vplen
);
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());
326 StackSegment
*seg
= fg
->segment();
327 seg
->setPreviousInMemory(currentSegment
);
328 currentSegment
= seg
;
331 cx
->pushSegmentAndFrame(seg
, *regs
);
332 seg
->setInitialVarObj(initialVarObj
);
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()
349 JS_ASSERT(cx_
->activeSegment() == segment());
350 JS_ASSERT(cx_
->maybefp() == fp());
351 cx_
->stack().popSegmentAndFrame(cx_
);
355 StackSpace::getExecuteFrame(JSContext
*cx
, JSScript
*script
, ExecuteFrameGuard
*fg
) const
357 return getSegmentAndFrame(cx
, 2, script
->nfixed
, fg
);
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
;
367 fg
->regs_
.sp
= fp
->base();
368 pushSegmentAndFrame(cx
, initialVarObj
, &fg
->regs_
, fg
);
372 StackSpace::pushDummyFrame(JSContext
*cx
, JSObject
&scopeChain
, DummyFrameGuard
*fg
)
374 if (!getSegmentAndFrame(cx
, 0 /*vplen*/, 0 /*nfixed*/, fg
))
376 fg
->fp()->initDummyFrame(cx
, scopeChain
);
377 fg
->regs_
.fp
= fg
->fp();
379 fg
->regs_
.sp
= fg
->fp()->slots();
380 pushSegmentAndFrame(cx
, NULL
/*varobj*/, &fg
->regs_
, fg
);
385 StackSpace::getGeneratorFrame(JSContext
*cx
, uintN vplen
, uintN nfixed
, GeneratorFrameGuard
*fg
)
387 return getSegmentAndFrame(cx
, vplen
, nfixed
, fg
);
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
);
399 StackSpace::bumpCommitAndLimit(JSStackFrame
*base
, Value
*sp
, uintN nvals
, Value
**limit
) const
401 JS_ASSERT(sp
>= firstUnused());
402 JS_ASSERT(sp
+ nvals
>= *limit
);
404 if (commitEnd
<= *limit
) {
405 Value
*quotaEnd
= (Value
*)base
+ STACK_QUOTA
;
406 if (sp
+ nvals
< quotaEnd
) {
407 if (!ensureSpace(NULL
, sp
, nvals
))
409 *limit
= Min(quotaEnd
, commitEnd
);
418 FrameRegsIter::initSlow()
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
440 FrameRegsIter::incSlow(JSStackFrame
*fp
, JSStackFrame
*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();
457 if (f
== curseg
->getInitialFrame()) {
458 curseg
= curseg
->getPreviousInContext();
459 cursp
= curseg
->getSuspendedRegs()->sp
;
460 f
= curseg
->getSuspendedFrame();
462 cursp
= f
->formalArgsEnd();
468 AllFramesIter::AllFramesIter(JSContext
*cx
)
469 : curcs(cx
->stack().getCurrentSegment()),
470 curfp(curcs
? curcs
->getCurrentFrame() : NULL
)
475 AllFramesIter::operator++()
478 if (curfp
== curcs
->getInitialFrame()) {
479 curcs
= curcs
->getPreviousInMemory();
480 curfp
= curcs
? curcs
->getCurrentFrame() : NULL
;
482 curfp
= curfp
->prev();
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);
495 if (!stackSpace
.init())
497 dtoaState
= js_NewDtoaState();
502 nativeStackBase
= GetNativeStackBase();
505 /* Set the default size for the code cache to 16MB. */
506 maxCodeCacheBytes
= 16 * 1024 * 1024;
513 JSThreadData::finish()
516 js_DestroyDtoaState(dtoaState
);
518 js_FinishGSNCache(&gsnCache
);
519 propertyCache
.~PropertyCache();
524 JSThreadData::mark(JSTracer
*trc
)
526 stackSpace
.mark(trc
);
530 JSThreadData::purge(JSContext
*cx
)
532 js_PurgeGSNCache(&gsnCache
);
534 /* FIXME: bug 506341. */
535 propertyCache
.purge(cx
);
545 JS_ASSERT(js_CurrentThreadId() == id
);
546 JSThread
*thread
= (JSThread
*) js_calloc(sizeof(JSThread
));
549 JS_INIT_CLIST(&thread
->contextList
);
551 if (!thread
->data
.init()) {
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
568 JS_ASSERT(!thread
->data
.conservativeGC
.hasStackToScan());
570 thread
->data
.finish();
575 js_CurrentThread(JSRuntime
*rt
)
577 void *id
= js_CurrentThreadId();
581 * We must not race with a GC that accesses cx->thread for JSContext
582 * instances on all threads, see bug 476934.
587 JSThread::Map::AddPtr p
= rt
->threads
.lookupForAdd(id
);
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();
599 thread
= NewThread(id
);
604 if (!rt
->threads
.relookupOrAdd(p
, id
, thread
)) {
606 DestroyThread(thread
);
610 /* Another thread cannot add an entry for the current thread id. */
611 JS_ASSERT(p
->value
== thread
);
613 JS_ASSERT(thread
->id
== id
);
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
);
628 js_InitContextThread(JSContext
*cx
)
630 JSThread
*thread
= js_CurrentThread(cx
->runtime
);
634 JS_APPEND_LINK(&cx
->threadLinks
, &thread
->contextList
);
640 js_ClearContextThread(JSContext
*cx
)
642 JS_ASSERT(CURRENT_THREAD_IS_ME(cx
->thread
));
643 JS_REMOVE_AND_INIT_LINK(&cx
->threadLinks
);
647 #endif /* JS_THREADSAFE */
650 js_CurrentThreadData(JSRuntime
*rt
)
653 JSThread
*thread
= js_CurrentThread(rt
);
657 return &thread
->data
;
659 return &rt
->threadData
;
664 js_InitThreads(JSRuntime
*rt
)
667 if (!rt
->threads
.init(4))
670 if (!rt
->threadData
.init())
677 js_FinishThreads(JSRuntime
*rt
)
680 if (!rt
->threads
.initialized())
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
);
689 rt
->threadData
.finish();
694 js_PurgeThreads(JSContext
*cx
)
697 for (JSThread::Map::Enum
e(cx
->runtime
->threads
);
700 JSThread
*thread
= e
.front().value
;
702 if (JS_CLIST_IS_EMPTY(&thread
->contextList
)) {
703 JS_ASSERT(cx
->thread
!= thread
);
705 DestroyThread(thread
);
708 thread
->data
.purge(cx
);
712 cx
->runtime
->threadData
.purge(cx
);
717 js_NewContext(JSRuntime
*rt
, size_t stackChunkSize
)
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
);
732 cx
= new (mem
) JSContext(rt
);
733 cx
->debugHooks
= &rt
->globalDebugHooks
;
734 #if JS_STACK_GROWTH_DIRECTION > 0
735 cx
->stackLimit
= (jsuword
) -1;
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);
750 if (!js_InitContextThread(cx
)) {
757 * Here the GC lock is still held after js_InitContextThread took it and
758 * the GC is not running on another thread.
761 if (rt
->state
== JSRTS_UP
) {
762 JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt
->contextList
));
766 if (rt
->state
== JSRTS_DOWN
) {
767 JS_ASSERT(JS_CLIST_IS_EMPTY(&rt
->contextList
));
769 rt
->state
= JSRTS_LAUNCHING
;
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
783 JS_APPEND_LINK(&cx
->link
, &rt
->contextList
);
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".
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
807 if (ok
&& !rt
->scriptFilenameTable
)
808 ok
= js_InitRuntimeScriptState(rt
);
810 ok
= js_InitRuntimeNumberState(cx
);
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
;
820 ok
= Shape::initRuntimeState(cx
);
821 if (rt
->shapeGen
< shapeGen
)
822 rt
->shapeGen
= shapeGen
;
829 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
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
);
844 /* Using ContextAllocPolicy, so init after JSContext is ready. */
845 if (!cx
->busyArrays
.init()) {
853 #if defined DEBUG && defined XP_UNIX
858 JSAutoFile() : mFile(NULL
) {}
865 FILE *open(const char *fname
, const char *mode
) {
866 return mFile
= fopen(fname
, mode
);
877 DumpEvalCacheMeter(JSContext
*cx
)
879 if (const char *filename
= getenv("JS_EVALCACHE_STATFILE")) {
884 #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
885 EVAL_CACHE_METER_LIST(frob
)
888 JSEvalCacheMeter
*ecm
= &cx
->compartment
->evalCacheMeter
;
890 static JSAutoFile fp
;
891 if (!fp
&& !fp
.open(filename
, "w"))
894 fprintf(fp
, "eval cache meter (%p):\n",
901 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(table
); ++i
) {
902 fprintf(fp
, "%-8.8s %llu\n",
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
);
911 # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
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
);
927 DumpFunctionMeter(JSContext
*cx
)
929 if (const char *filename
= cx
->runtime
->functionMeterFilename
) {
934 #define frob(x) { #x, offsetof(JSFunctionMeter, x) }
935 FUNCTION_KIND_METER_LIST(frob
)
938 JSFunctionMeter
*fm
= &cx
->runtime
->functionMeter
;
940 static JSAutoFile fp
;
941 if (!fp
&& !fp
.open(filename
, "w"))
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
);
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)
964 #ifndef DUMP_FUNCTION_METER
965 # define DUMP_FUNCTION_METER(cx) ((void) 0)
969 js_DestroyContext(JSContext
*cx
, JSDestroyContextMode mode
)
972 JSContextCallback cxCallback
;
975 JS_ASSERT(!cx
->enumerators
);
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
));
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
);
997 if (mode
!= JSDCM_NEW_FAILED
) {
998 cxCallback
= rt
->cxCallback
;
1001 * JSCONTEXT_DESTROY callback is not allowed to fail and must
1005 JSBool callbackStatus
=
1007 cxCallback(cx
, JSCONTEXT_DESTROY
);
1008 JS_ASSERT(callbackStatus
);
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)
1022 JS_REMOVE_LINK(&cx
->link
);
1023 last
= (rt
->contextList
.next
== &rt
->contextList
);
1025 rt
->state
= JSRTS_LANDING
;
1026 if (last
|| mode
== JSDCM_FORCE_GC
|| mode
== JSDCM_MAYBE_GC
1027 #ifdef JS_THREADSAFE
1028 || cx
->outstandingRequests
!= 0
1031 JS_ASSERT(!rt
->gcRunning
);
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
);
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)
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. */
1081 rt
->state
= JSRTS_DOWN
;
1082 JS_NOTIFY_ALL_CONDVAR(rt
->stateChange
);
1084 if (mode
== JSDCM_FORCE_GC
)
1085 js_GC(cx
, NULL
, GC_NORMAL
);
1086 else if (mode
== JSDCM_MAYBE_GC
)
1092 #ifdef JS_THREADSAFE
1094 JSThread
*t
= cx
->thread
;
1096 js_ClearContextThread(cx
);
1097 JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t
->contextList
), !t
->data
.requestDepth
);
1099 #ifdef JS_METER_DST_OFFSET_CACHING
1100 cx
->dstOffsetCache
.dumpStats();
1107 FreeContext(JSContext
*cx
)
1109 #ifdef JS_THREADSAFE
1110 JS_ASSERT(!cx
->thread
);
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
;
1124 JSArgumentFormatMap
*temp
= map
;
1129 /* Destroy the resolve recursion damper. */
1130 if (cx
->resolvingTable
) {
1131 JS_DHashTableDestroy(cx
->resolvingTable
);
1132 cx
->resolvingTable
= NULL
;
1135 /* Finally, free cx itself. */
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
)
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
)
1164 return js_ContextIterator(rt
, JS_FALSE
, &iter
);
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
);
1177 resolving_MatchEntry(JSDHashTable
*table
,
1178 const JSDHashEntryHdr
*hdr
,
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
= {
1191 resolving_MatchEntry
,
1192 JS_DHashMoveEntryStub
,
1193 JS_DHashClearEntryStub
,
1194 JS_DHashFinalizeStub
,
1199 js_StartResolving(JSContext
*cx
, JSResolvingKey
*key
, uint32 flag
,
1200 JSResolvingEntry
**entryp
)
1202 JSDHashTable
*table
;
1203 JSResolvingEntry
*entry
;
1205 table
= cx
->resolvingTable
;
1207 table
= JS_NewDHashTable(&resolving_dhash_ops
, NULL
,
1208 sizeof(JSResolvingEntry
),
1212 cx
->resolvingTable
= table
;
1215 entry
= (JSResolvingEntry
*)
1216 JS_DHashTableOperate(table
, key
, JS_DHASH_ADD
);
1220 if (entry
->flags
& flag
) {
1221 /* An entry for (key, flag) exists already -- dampen recursion. */
1224 /* Fill in key if we were the first to add entry, then set flag. */
1225 if (!entry
->key
.obj
)
1227 entry
->flags
|= flag
;
1233 JS_ReportOutOfMemory(cx
);
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
;
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
);
1267 JS_DHashTableOperate(table
, key
, JS_DHASH_REMOVE
);
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.
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 */
1300 hook(cx
, message
, reportp
, cx
->debugHooks
->debugErrorHookData
);
1304 /* The report must be initially zeroed. */
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()) {
1314 report
->filename
= fp
->script()->filename
;
1315 report
->lineno
= js_FramePCToLineNumber(cx
, fp
);
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.
1329 js_ReportOutOfMemory(JSContext
*cx
)
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
)
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. */
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();
1362 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
1364 !hook(cx
, msg
, &report
, cx
->debugHooks
->debugErrorHookData
)) {
1370 onError(cx
, msg
, &report
);
1374 js_ReportOutOfScriptQuota(JSContext
*cx
)
1376 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1377 JSMSG_SCRIPT_STACK_QUOTA
);
1381 js_ReportOverRecursed(JSContext
*cx
)
1383 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_OVER_RECURSED
);
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.
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
;
1414 } else if (JSREPORT_IS_STRICT(*flags
)) {
1415 /* Warning/error only when JSOPTION_STRICT is set. */
1416 if (!JS_HAS_STRICT_OPTION(cx
))
1420 /* Warnings become errors when JSOPTION_WERROR is set. */
1421 if (JSREPORT_IS_WARNING(*flags
) && JS_HAS_WERROR_OPTION(cx
))
1422 *flags
&= ~JSREPORT_WARNING
;
1428 js_ReportErrorVA(JSContext
*cx
, uintN flags
, const char *format
, va_list ap
)
1433 JSErrorReport report
;
1436 if (checkReportFlags(cx
, &flags
))
1439 message
= JS_vsmprintf(format
, ap
);
1442 messagelen
= strlen(message
);
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
);
1454 cx
->free(ucmessage
);
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).
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
;
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
);
1485 efs
= callback(userRef
, NULL
, errorNumber
);
1487 size_t totalArgsLength
= 0;
1488 size_t argLengths
[10]; /* only {0} thru {9} supported */
1489 argCount
= efs
->argCount
;
1490 JS_ASSERT(argCount
<= 10);
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
1498 reportp
->messageArgs
= (const jschar
**)
1499 cx
->malloc(sizeof(jschar
*) * (argCount
+ 1));
1500 if (!reportp
->messageArgs
)
1502 reportp
->messageArgs
[argCount
] = NULL
;
1503 for (i
= 0; i
< argCount
; i
++) {
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
])
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.
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
);
1534 expandedLength
= len
1535 - (3 * argCount
) /* exclude the {n} */
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
));
1550 if (isdigit(fmt
[1])) {
1551 int d
= JS7_UNDEC(fmt
[1]);
1552 JS_ASSERT(d
< argCount
);
1553 js_strncpy(out
, reportp
->messageArgs
[d
],
1555 out
+= argLengths
[d
];
1563 JS_ASSERT(expandedArgs
== argCount
);
1567 js_DeflateString(cx
, reportp
->ucmessage
,
1568 (size_t)(out
- reportp
->ucmessage
));
1574 * Zero arguments: the format string (if it exists) is the
1579 *messagep
= JS_strdup(cx
, efs
->format
);
1582 len
= strlen(*messagep
);
1583 reportp
->ucmessage
= js_InflateString(cx
, *messagep
, &len
);
1584 if (!reportp
->ucmessage
)
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
);
1597 JS_snprintf(*messagep
, nbytes
, defaultErrorMessage
, errorNumber
);
1602 if (reportp
->messageArgs
) {
1603 /* free the arguments only if we allocated them */
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
;
1617 cx
->free((void *)*messagep
);
1624 js_ReportErrorNumberVA(JSContext
*cx
, uintN flags
, JSErrorCallback callback
,
1625 void *userRef
, const uintN errorNumber
,
1626 JSBool charArgs
, va_list ap
)
1628 JSErrorReport report
;
1632 if (checkReportFlags(cx
, &flags
))
1634 warning
= JSREPORT_IS_WARNING(flags
);
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
)) {
1646 ReportError(cx
, message
, &report
, callback
, userRef
);
1650 if (report
.messageArgs
) {
1652 * js_ExpandErrorArguments owns its messageArgs only if it had to
1653 * inflate the arguments (from regular |char *|s).
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
);
1669 js_ReportErrorAgain(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
1671 JSErrorReporter onError
;
1676 if (cx
->lastMessage
)
1677 js_free(cx
->lastMessage
);
1678 cx
->lastMessage
= JS_strdup(cx
, message
);
1679 if (!cx
->lastMessage
)
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.
1688 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
1690 !hook(cx
, cx
->lastMessage
, reportp
,
1691 cx
->debugHooks
->debugErrorHookData
)) {
1696 onError(cx
, cx
->lastMessage
, reportp
);
1700 js_ReportIsNotDefined(JSContext
*cx
, const char *name
)
1702 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_DEFINED
, name
);
1706 js_ReportIsNullOrUndefined(JSContext
*cx
, intN spindex
, const Value
&v
,
1712 bytes
= DecompileValueGenerator(cx
, spindex
, v
, fallback
);
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
,
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
);
1728 JS_ASSERT(v
.isNull());
1729 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1730 js_GetErrorMessage
, NULL
,
1731 JSMSG_UNEXPECTED_TYPE
, bytes
,
1740 js_ReportMissingArg(JSContext
*cx
, const Value
&v
, uintN arg
)
1746 JS_snprintf(argbuf
, sizeof argbuf
, "%u", arg
);
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
));
1755 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1756 JSMSG_MISSING_FUN_ARG
, argbuf
,
1757 bytes
? bytes
: "");
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
)
1769 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
>= 1);
1770 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
<= 3);
1771 bytes
= DecompileValueGenerator(cx
, spindex
, v
, fallback
);
1775 ok
= JS_ReportErrorFlagsAndNumber(cx
, flags
, js_GetErrorMessage
,
1776 NULL
, errorNumber
, bytes
, arg1
, arg2
);
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
; }
1787 JSErrorFormatString js_ErrorFormatString
[JSErr_Limit
] = {
1788 #define MSG_DEF(name, number, count, exception, format) \
1789 { format, count, exception } ,
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
];
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.
1817 td
->interruptFlags
= 0;
1818 #ifdef JS_THREADSAFE
1819 JS_ATOMIC_DECREMENT(&rt
->interruptCounter
);
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
;
1832 delayedOutOfMemory
= (rt
->gcBytes
> rt
->gcMaxBytes
);
1834 if (delayedOutOfMemory
) {
1835 js_ReportOutOfMemory(cx
);
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
);
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
);
1866 js_HandleExecutionInterrupt(JSContext
*cx
)
1868 JSBool result
= JS_TRUE
;
1869 if (JS_THREAD_DATA(cx
)->interruptFlags
)
1870 result
= js_InvokeOperationCallback(cx
) && result
;
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
1886 #ifdef JS_THREADSAFE
1887 JSThread
*thread
= cx
->thread
;
1892 td
= JS_THREAD_DATA(cx
);
1894 td
->triggerOperationCallback(cx
->runtime
);
1898 TriggerAllOperationCallbacks(JSRuntime
*rt
)
1900 for (ThreadDataIter
i(rt
); !i
.empty(); i
.popFront())
1901 i
.threadData()->triggerOperationCallback(rt
);
1904 } /* namespace js */
1907 js_GetScriptedCaller(JSContext
*cx
, JSStackFrame
*fp
)
1910 fp
= js_GetTopStackFrame(cx
);
1911 while (fp
&& fp
->isDummyFrame())
1913 JS_ASSERT_IF(fp
, fp
->isScriptFrame());
1918 js_GetCurrentBytecodePC(JSContext
* cx
)
1920 jsbytecode
*pc
, *imacpc
;
1923 if (JS_ON_TRACE(cx
)) {
1924 pc
= cx
->bailExit
->pc
;
1925 imacpc
= cx
->bailExit
->imacpc
;
1929 JS_ASSERT_NOT_ON_TRACE(cx
); /* for static analysis */
1930 pc
= cx
->regs
? cx
->regs
->pc
: 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
;
1945 js_CurrentPCIsInImacro(JSContext
*cx
)
1948 VOUCH_DOES_NOT_REQUIRE_STACK();
1949 if (JS_ON_TRACE(cx
))
1950 return cx
->bailExit
->imacpc
!= NULL
;
1951 return cx
->fp()->hasImacropc();
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;
1973 missIncreasing
= missDecreasing
= 0;
1974 missIncreasingOffsetChangeExpand
= missIncreasingOffsetChangeUpper
= 0;
1975 missDecreasingOffsetChangeExpand
= missDecreasingOffsetChangeLower
= 0;
1976 missLargeIncrease
= missLargeDecrease
= 0;
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()
1993 JSContext::JSContext(JSRuntime
*rt
)
1995 compartment(rt
->defaultCompartment
),
1997 busyArrays(thisInInitializer())
2001 JSContext::resetCompartment()
2005 scopeobj
= &fp()->scopeChain();
2007 scopeobj
= globalObject
;
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
);
2020 compartment
= scopeobj
->compartment();
2022 if (isExceptionPending())
2023 wrapPendingException();
2029 * If we try to use the context without a selected compartment,
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.
2042 JSContext::wrapPendingException()
2044 Value v
= getPendingException();
2045 clearPendingException();
2046 if (compartment
->wrap(this, &v
))
2047 setPendingException(v
);
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
);
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
);
2073 setCurrentRegs(currentSegment
->getSuspendedRegs());
2074 currentSegment
->resume();
2077 JS_ASSERT(regs
->fp
->prev() == NULL
);
2078 setCurrentRegs(NULL
);
2083 JSContext::saveActiveSegment()
2085 JS_ASSERT(hasActiveSegment());
2086 currentSegment
->save(regs
);
2087 setCurrentRegs(NULL
);
2091 JSContext::restoreSegment()
2093 js::StackSegment
*ccs
= currentSegment
;
2094 setCurrentRegs(ccs
->getSuspendedRegs());
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
)
2113 JS_NOT_REACHED("no matching generator");
2118 JSContext::containingSegment(const JSStackFrame
*target
)
2120 /* The context may have nothing running. */
2121 StackSegment
*seg
= currentSegment
;
2125 /* The active segments's top frame is cx->regs->fp. */
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()) {
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()) {
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.
2166 JS_FRIEND_API(void *)
2167 JSRuntime::onOutOfMemory(void *p
, size_t nbytes
, JSContext
*cx
)
2169 #ifdef JS_THREADSAFE
2170 gcHelperThread
.waitBackgroundSweepEnd(this);
2172 p
= ::js_malloc(nbytes
);
2173 else if (p
== reinterpret_cast<void *>(1))
2174 p
= ::js_calloc(nbytes
);
2176 p
= ::js_realloc(p
, nbytes
);
2181 js_ReportOutOfMemory(cx
);
2186 * Release pool's arenas if the stackPool has existed for longer than the
2187 * limit specified by gcEmptyArenaPoolLifespan.
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
);
2203 FreeOldArenas(runtime
, ®ExpPool
);
2207 ComputeIsJITBroken()
2212 if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
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'",
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");
2230 // We're using 2.6.29, and this causes trouble with the JITs on i9000.
2232 bool broken
= false;
2233 std::ifstream
cpuinfo("/proc/cpuinfo");
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
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
);
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 ");
2261 #endif // ifndef ANDROID
2267 static bool computedIsBroken
= false;
2268 static bool isBroken
= false;
2269 if (!computedIsBroken
) {
2270 isBroken
= ComputeIsJITBroken();
2271 computedIsBroken
= true;
2277 JSContext::updateJITEnabled()
2280 traceJitEnabled
= ((options
& JSOPTION_JIT
) &&
2281 !IsJITBrokenHere() &&
2282 (debugHooks
== &js_NullDebugHooks
||
2283 (debugHooks
== &runtime
->globalDebugHooks
&&
2284 !runtime
->debuggerInhibitsJIT())));
2287 methodJitEnabled
= (options
& JSOPTION_METHODJIT
) &&
2289 # if defined JS_CPU_X86 || defined JS_CPU_X64
2290 && JSC::MacroAssemblerX86Common::getSSEState() >=
2291 JSC::MacroAssemblerX86Common::HasSSE2
2295 profilingEnabled
= (options
& JSOPTION_PROFILING
) && traceJitEnabled
&& methodJitEnabled
;
2302 JS_FORCES_STACK
JS_FRIEND_API(void)
2303 LeaveTrace(JSContext
*cx
)
2306 if (JS_ON_TRACE(cx
))
2311 } /* namespace js */