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 "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
55 #include "jsversion.h"
68 #include "jsstaticcheck.h"
73 FreeContext(JSContext
*cx
);
76 InitThreadData(JSThreadData
*data
)
79 /* The data must be already zeroed. */
80 for (size_t i
= 0; i
!= sizeof(*data
); ++i
)
81 JS_ASSERT(reinterpret_cast<uint8
*>(data
)[i
] == 0);
84 js_InitJIT(&data
->traceMonitor
);
89 FinishThreadData(JSThreadData
*data
)
92 /* All GC-related things must be already removed at this point. */
93 for (size_t i
= 0; i
!= JS_ARRAY_LENGTH(data
->scriptsToGC
); ++i
)
94 JS_ASSERT(!data
->scriptsToGC
[i
]);
97 js_FinishGSNCache(&data
->gsnCache
);
98 js_FinishPropertyCache(&data
->propertyCache
);
100 js_FinishJIT(&data
->traceMonitor
);
105 PurgeThreadData(JSContext
*cx
, JSThreadData
*data
)
107 js_PurgeGSNCache(&data
->gsnCache
);
109 /* FIXME: bug 506341. */
110 js_PurgePropertyCache(cx
, &data
->propertyCache
);
113 JSTraceMonitor
*tm
= &data
->traceMonitor
;
114 tm
->reservedDoublePoolPtr
= tm
->reservedDoublePool
;
117 * FIXME: bug 506117. We should flush only if (cx->runtime->gcRegenShapes),
118 * but we can't yet, because traces may embed sprop and object references,
119 * and we don't yet mark such embedded refs.
121 tm
->needFlush
= JS_TRUE
;
124 tm
->recorder
->deepAbort();
127 * We want to keep tm->reservedObjects after the GC. So, unless we are
128 * shutting down, we don't purge them here and rather mark them during
129 * the GC, see MarkReservedObjects in jsgc.cpp.
131 if (cx
->runtime
->state
== JSRTS_LANDING
)
132 tm
->reservedObjects
= NULL
;
135 /* Destroy eval'ed scripts. */
136 js_DestroyScriptsToGC(cx
, data
);
144 JS_ASSERT(js_CurrentThreadId() == id
);
145 JSThread
*thread
= (JSThread
*) js_calloc(sizeof(JSThread
));
148 JS_INIT_CLIST(&thread
->contextList
);
150 InitThreadData(&thread
->data
);
155 DestroyThread(JSThread
*thread
)
157 /* The thread must have zero contexts. */
158 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread
->contextList
));
159 JS_ASSERT(!thread
->titleToShare
);
160 FinishThreadData(&thread
->data
);
165 js_InitContextThread(JSContext
*cx
)
167 JS_ASSERT(!cx
->thread
);
168 jsword id
= js_CurrentThreadId();
169 JSRuntime
*rt
= cx
->runtime
;
173 * We must not race with a GC that accesses cx->thread for JSContext
174 * instances on all threads, see bug 476934.
177 JSThreadsHashEntry
*entry
= (JSThreadsHashEntry
*)
178 JS_DHashTableOperate(&rt
->threads
,
182 if (JS_DHASH_ENTRY_IS_BUSY(&entry
->base
)) {
183 thread
= entry
->thread
;
184 JS_ASSERT(thread
->id
== id
);
187 thread
= NewThread(id
);
192 entry
= (JSThreadsHashEntry
*)
193 JS_DHashTableOperate(&rt
->threads
, (const void *) id
,
197 DestroyThread(thread
);
201 /* Another thread cannot initialize entry->thread. */
202 JS_ASSERT(!entry
->thread
);
203 entry
->thread
= thread
;
206 JS_APPEND_LINK(&cx
->threadLinks
, &thread
->contextList
);
212 js_ClearContextThread(JSContext
*cx
)
214 JS_ASSERT(CURRENT_THREAD_IS_ME(cx
->thread
));
215 JS_REMOVE_AND_INIT_LINK(&cx
->threadLinks
);
220 thread_matchEntry(JSDHashTable
*table
,
221 const JSDHashEntryHdr
*hdr
,
224 const JSThreadsHashEntry
*entry
= (const JSThreadsHashEntry
*) hdr
;
226 return entry
->thread
->id
== (jsword
) key
;
229 static const JSDHashTableOps threads_ops
= {
232 JS_DHashVoidPtrKeyStub
,
234 JS_DHashMoveEntryStub
,
235 JS_DHashClearEntryStub
,
236 JS_DHashFinalizeStub
,
240 static JSDHashOperator
241 thread_destroyer(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
, uint32
/* index */,
244 JSThreadsHashEntry
*entry
= (JSThreadsHashEntry
*) hdr
;
245 JSThread
*thread
= entry
->thread
;
247 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread
->contextList
));
248 DestroyThread(thread
);
249 return JS_DHASH_REMOVE
;
252 static JSDHashOperator
253 thread_purger(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
, uint32
/* index */,
256 JSContext
* cx
= (JSContext
*) arg
;
257 JSThread
*thread
= ((JSThreadsHashEntry
*) hdr
)->thread
;
259 if (JS_CLIST_IS_EMPTY(&thread
->contextList
)) {
260 JS_ASSERT(cx
->thread
!= thread
);
261 js_DestroyScriptsToGC(cx
, &thread
->data
);
262 DestroyThread(thread
);
263 return JS_DHASH_REMOVE
;
265 PurgeThreadData(cx
, &thread
->data
);
266 memset(thread
->gcFreeLists
, 0, sizeof(thread
->gcFreeLists
));
267 return JS_DHASH_NEXT
;
270 #endif /* JS_THREADSAFE */
273 js_InitThreads(JSRuntime
*rt
)
276 if (!JS_DHashTableInit(&rt
->threads
, &threads_ops
, NULL
,
277 sizeof(JSThreadsHashEntry
), 4)) {
278 rt
->threads
.ops
= NULL
;
282 InitThreadData(&rt
->threadData
);
288 js_FinishThreads(JSRuntime
*rt
)
291 if (!rt
->threads
.ops
)
293 JS_DHashTableEnumerate(&rt
->threads
, thread_destroyer
, NULL
);
294 JS_DHashTableFinish(&rt
->threads
);
295 rt
->threads
.ops
= NULL
;
297 FinishThreadData(&rt
->threadData
);
302 js_PurgeThreads(JSContext
*cx
)
305 JS_DHashTableEnumerate(&cx
->runtime
->threads
, thread_purger
, cx
);
307 PurgeThreadData(cx
, &cx
->runtime
->threadData
);
312 * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version
313 * associated with scripts, so in addition to storing them in cx->options we
314 * duplicate them in cx->version (script->version, etc.) and ensure each bit
315 * remains synchronized between the two through these two functions.
318 js_SyncOptionsToVersion(JSContext
* cx
)
320 if (cx
->options
& JSOPTION_XML
)
321 cx
->version
|= JSVERSION_HAS_XML
;
323 cx
->version
&= ~JSVERSION_HAS_XML
;
324 if (cx
->options
& JSOPTION_ANONFUNFIX
)
325 cx
->version
|= JSVERSION_ANONFUNFIX
;
327 cx
->version
&= ~JSVERSION_ANONFUNFIX
;
331 js_SyncVersionToOptions(JSContext
* cx
)
333 if (cx
->version
& JSVERSION_HAS_XML
)
334 cx
->options
|= JSOPTION_XML
;
336 cx
->options
&= ~JSOPTION_XML
;
337 if (cx
->version
& JSVERSION_ANONFUNFIX
)
338 cx
->options
|= JSOPTION_ANONFUNFIX
;
340 cx
->options
&= ~JSOPTION_ANONFUNFIX
;
344 js_OnVersionChange(JSContext
*cx
)
347 JSVersion version
= JSVERSION_NUMBER(cx
);
349 JS_ASSERT(version
== JSVERSION_DEFAULT
|| version
>= JSVERSION_ECMA_3
);
354 js_SetVersion(JSContext
*cx
, JSVersion version
)
356 cx
->version
= version
;
357 js_SyncVersionToOptions(cx
);
358 js_OnVersionChange(cx
);
362 js_NewContext(JSRuntime
*rt
, size_t stackChunkSize
)
366 JSContextCallback cxCallback
;
369 * We need to initialize the new context fully before adding it to the
370 * runtime list. After that it can be accessed from another thread via
371 * js_ContextIterator.
373 cx
= (JSContext
*) js_calloc(sizeof *cx
);
378 cx
->debugHooks
= &rt
->globalDebugHooks
;
379 #if JS_STACK_GROWTH_DIRECTION > 0
380 cx
->stackLimit
= (jsuword
) -1;
382 cx
->scriptStackQuota
= JS_DEFAULT_SCRIPT_STACK_QUOTA
;
383 JS_STATIC_ASSERT(JSVERSION_DEFAULT
== 0);
384 JS_ASSERT(cx
->version
== JSVERSION_DEFAULT
);
385 VOUCH_DOES_NOT_REQUIRE_STACK();
386 JS_INIT_ARENA_POOL(&cx
->stackPool
, "stack", stackChunkSize
, sizeof(jsval
),
387 &cx
->scriptStackQuota
);
389 JS_INIT_ARENA_POOL(&cx
->tempPool
, "temp",
390 1024, /* FIXME: bug 421435 */
391 sizeof(jsdouble
), &cx
->scriptStackQuota
);
393 js_InitRegExpStatics(cx
);
394 JS_ASSERT(cx
->resolveFlags
== 0);
396 if (!js_InitContextBusyArrayTable(cx
)) {
402 if (!js_InitContextThread(cx
)) {
409 * Here the GC lock is still held after js_InitContextThread took it and
410 * the GC is not running on another thread.
413 if (rt
->state
== JSRTS_UP
) {
414 JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt
->contextList
));
418 if (rt
->state
== JSRTS_DOWN
) {
419 JS_ASSERT(JS_CLIST_IS_EMPTY(&rt
->contextList
));
421 rt
->state
= JSRTS_LAUNCHING
;
424 JS_WAIT_CONDVAR(rt
->stateChange
, JS_NO_TIMEOUT
);
427 * During the above wait after we are notified about the state change
428 * but before we wake up, another thread could enter the GC from
429 * js_DestroyContext, bug 478336. So we must wait here to ensure that
430 * when we exit the loop with the first flag set to true, that GC is
435 JS_APPEND_LINK(&cx
->link
, &rt
->contextList
);
439 * If cx is the first context on this runtime, initialize well-known atoms,
440 * keywords, numbers, and strings. If one of these steps should fail, the
441 * runtime will be left in a partially initialized state, with zeroes and
442 * nulls stored in the default-initialized remainder of the struct. We'll
443 * clean the runtime up under js_DestroyContext, because cx will be "last"
444 * as well as "first".
450 ok
= js_InitCommonAtoms(cx
);
453 * scriptFilenameTable may be left over from a previous episode of
454 * non-zero contexts alive in rt, so don't re-init the table if it's
457 if (ok
&& !rt
->scriptFilenameTable
)
458 ok
= js_InitRuntimeScriptState(rt
);
460 ok
= js_InitRuntimeNumberState(cx
);
462 ok
= js_InitRuntimeStringState(cx
);
467 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
472 rt
->state
= JSRTS_UP
;
473 JS_NOTIFY_ALL_CONDVAR(rt
->stateChange
);
477 cxCallback
= rt
->cxCallback
;
478 if (cxCallback
&& !cxCallback(cx
, JSCONTEXT_NEW
)) {
479 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
486 #if defined DEBUG && defined XP_UNIX
491 JSAutoFile() : mFile(NULL
) {}
498 FILE *open(const char *fname
, const char *mode
) {
499 return mFile
= fopen(fname
, mode
);
509 #ifdef JS_EVAL_CACHE_METERING
511 DumpEvalCacheMeter(JSContext
*cx
)
517 #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
518 EVAL_CACHE_METER_LIST(frob
)
521 JSEvalCacheMeter
*ecm
= &JS_THREAD_DATA(cx
)->evalCacheMeter
;
523 static JSAutoFile fp
;
525 fp
.open("/tmp/evalcache.stats", "w");
530 fprintf(fp
, "eval cache meter (%p):\n",
537 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(table
); ++i
) {
538 fprintf(fp
, "%-8.8s %llu\n",
539 table
[i
].name
, *(uint64
*)((uint8
*)ecm
+ table
[i
].offset
));
541 fprintf(fp
, "hit ratio %g%%\n", ecm
->hit
* 100. / ecm
->probe
);
542 fprintf(fp
, "avg steps %g\n", double(ecm
->step
) / ecm
->probe
);
545 # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
548 #ifdef JS_FUNCTION_METERING
550 DumpFunctionMeter(JSContext
*cx
)
556 #define frob(x) { #x, offsetof(JSFunctionMeter, x) }
557 FUNCTION_KIND_METER_LIST(frob
)
560 JSFunctionMeter
*fm
= &cx
->runtime
->functionMeter
;
562 static JSAutoFile fp
;
564 fp
.open("/tmp/function.stats", "a");
569 fprintf(fp
, "function meter (%s):\n", cx
->runtime
->lastScriptFilename
);
570 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(table
); ++i
) {
571 fprintf(fp
, "%-11.11s %d\n",
572 table
[i
].name
, *(int32
*)((uint8
*)fm
+ table
[i
].offset
));
576 # define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
579 #endif /* DEBUG && XP_UNIX */
581 #ifndef DUMP_EVAL_CACHE_METER
582 # define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
585 #ifndef DUMP_FUNCTION_METER
586 # define DUMP_FUNCTION_METER(cx) ((void) 0)
590 js_DestroyContext(JSContext
*cx
, JSDestroyContextMode mode
)
593 JSContextCallback cxCallback
;
599 * For API compatibility we allow to destroy contexts without a thread in
600 * optimized builds. We assume that the embedding knows that an OOM error
601 * cannot happen in JS_SetContextThread.
603 JS_ASSERT(cx
->thread
&& CURRENT_THREAD_IS_ME(cx
->thread
));
605 JS_SetContextThread(cx
);
607 JS_ASSERT_IF(rt
->gcRunning
, cx
->outstandingRequests
== 0);
610 if (mode
!= JSDCM_NEW_FAILED
) {
611 cxCallback
= rt
->cxCallback
;
614 * JSCONTEXT_DESTROY callback is not allowed to fail and must
618 JSBool callbackStatus
=
620 cxCallback(cx
, JSCONTEXT_DESTROY
);
621 JS_ASSERT(callbackStatus
);
626 JS_ASSERT(rt
->state
== JSRTS_UP
|| rt
->state
== JSRTS_LAUNCHING
);
629 * Typically we are called outside a request, so ensure that the GC is not
630 * running before removing the context from rt->contextList, see bug 477021.
632 if (cx
->requestDepth
== 0)
635 JS_REMOVE_LINK(&cx
->link
);
636 last
= (rt
->contextList
.next
== &rt
->contextList
);
638 rt
->state
= JSRTS_LANDING
;
639 if (last
|| mode
== JSDCM_FORCE_GC
|| mode
== JSDCM_MAYBE_GC
641 || cx
->requestDepth
!= 0
644 JS_ASSERT(!rt
->gcRunning
);
651 * If cx is not in a request already, begin one now so that we wait
652 * for any racing GC started on a not-last context to finish, before
653 * we plow ahead and unpin atoms. Note that even though we begin a
654 * request here if necessary, we end all requests on cx below before
655 * forcing a final GC. This lets any not-last context destruction
656 * racing in another thread try to force or maybe run the GC, but by
657 * that point, rt->state will not be JSRTS_UP, and that GC attempt
660 if (cx
->requestDepth
== 0)
664 /* Unlock and clear GC things held by runtime pointers. */
665 js_FinishRuntimeNumberState(cx
);
666 js_FinishRuntimeStringState(cx
);
668 /* Unpin all common atoms before final GC. */
669 js_FinishCommonAtoms(cx
);
671 /* Clear debugging state to remove GC roots. */
672 JS_ClearAllTraps(cx
);
673 JS_ClearAllWatchPoints(cx
);
676 /* Remove more GC roots in regExpStatics, then collect garbage. */
677 JS_ClearRegExpRoots(cx
);
681 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
682 * end our request here in case we are "last" -- in that event, another
683 * js_DestroyContext that was not last might be waiting in the GC for our
684 * request to end. We'll let it run below, just before we do the truly
685 * final GC and then free atom state.
687 while (cx
->requestDepth
!= 0)
692 /* Clear builtin functions, which are recreated on demand. */
693 memset(rt
->builtinFunctions
, 0, sizeof rt
->builtinFunctions
);
695 js_GC(cx
, GC_LAST_CONTEXT
);
696 DUMP_EVAL_CACHE_METER(cx
);
697 DUMP_FUNCTION_METER(cx
);
700 * Free the script filename table if it exists and is empty. Do this
701 * after the last GC to avoid finalizers tripping on free memory.
703 if (rt
->scriptFilenameTable
&&
704 rt
->scriptFilenameTable
->nentries
== 0) {
705 js_FinishRuntimeScriptState(rt
);
708 /* Take the runtime down, now that it has no contexts or atoms. */
710 rt
->state
= JSRTS_DOWN
;
711 JS_NOTIFY_ALL_CONDVAR(rt
->stateChange
);
713 if (mode
== JSDCM_FORCE_GC
)
714 js_GC(cx
, GC_NORMAL
);
715 else if (mode
== JSDCM_MAYBE_GC
)
722 js_ClearContextThread(cx
);
729 FreeContext(JSContext
*cx
)
731 JSArgumentFormatMap
*map
;
732 JSLocalRootStack
*lrs
;
733 JSLocalRootChunk
*lrc
;
736 JS_ASSERT(!cx
->thread
);
739 /* Free the stuff hanging off of cx. */
740 js_FreeRegExpStatics(cx
);
741 VOUCH_DOES_NOT_REQUIRE_STACK();
742 JS_FinishArenaPool(&cx
->stackPool
);
743 JS_FinishArenaPool(&cx
->tempPool
);
746 js_free(cx
->lastMessage
);
748 /* Remove any argument formatters. */
749 map
= cx
->argumentFormatMap
;
751 JSArgumentFormatMap
*temp
= map
;
756 /* Destroy the busy array table. */
757 if (cx
->busyArrayTable
) {
758 JS_HashTableDestroy(cx
->busyArrayTable
);
759 cx
->busyArrayTable
= NULL
;
762 /* Destroy the resolve recursion damper. */
763 if (cx
->resolvingTable
) {
764 JS_DHashTableDestroy(cx
->resolvingTable
);
765 cx
->resolvingTable
= NULL
;
768 lrs
= cx
->localRootStack
;
770 while ((lrc
= lrs
->topChunk
) != &lrs
->firstChunk
) {
771 lrs
->topChunk
= lrc
->down
;
777 /* Finally, free cx itself. */
782 js_ValidContextPointer(JSRuntime
*rt
, JSContext
*cx
)
786 for (cl
= rt
->contextList
.next
; cl
!= &rt
->contextList
; cl
= cl
->next
) {
790 JS_RUNTIME_METER(rt
, deadContexts
);
795 js_ContextIterator(JSRuntime
*rt
, JSBool unlocked
, JSContext
**iterp
)
797 JSContext
*cx
= *iterp
;
801 cx
= js_ContextFromLinkField(cx
? cx
->link
.next
: rt
->contextList
.next
);
802 if (&cx
->link
== &rt
->contextList
)
810 JS_FRIEND_API(JSContext
*)
811 js_NextActiveContext(JSRuntime
*rt
, JSContext
*cx
)
813 JSContext
*iter
= cx
;
815 while ((cx
= js_ContextIterator(rt
, JS_FALSE
, &iter
)) != NULL
) {
816 if (cx
->requestDepth
)
821 return js_ContextIterator(rt
, JS_FALSE
, &iter
);
828 js_CountThreadRequests(JSContext
*cx
)
830 JSCList
*head
, *link
;
833 JS_ASSERT(CURRENT_THREAD_IS_ME(cx
->thread
));
834 head
= &cx
->thread
->contextList
;
836 for (link
= head
->next
; link
!= head
; link
= link
->next
) {
837 JSContext
*acx
= CX_FROM_THREAD_LINKS(link
);
838 JS_ASSERT(acx
->thread
== cx
->thread
);
839 if (acx
->requestDepth
)
846 * If the GC is running and we're called on another thread, wait for this GC
847 * activation to finish. We can safely wait here without fear of deadlock (in
848 * the case where we are called within a request on another thread's context)
849 * because the GC doesn't set rt->gcRunning until after it has waited for all
850 * active requests to end.
852 * We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid
853 * expensive calls when the GC is not running.
856 js_WaitForGC(JSRuntime
*rt
)
858 JS_ASSERT_IF(rt
->gcRunning
, rt
->gcLevel
> 0);
859 if (rt
->gcRunning
&& rt
->gcThread
->id
!= js_CurrentThreadId()) {
861 JS_AWAIT_GC_DONE(rt
);
862 } while (rt
->gcRunning
);
867 js_DiscountRequestsForGC(JSContext
*cx
)
871 JS_ASSERT(cx
->thread
);
872 JS_ASSERT(cx
->runtime
->gcThread
!= cx
->thread
);
875 if (JS_ON_TRACE(cx
)) {
876 JS_UNLOCK_GC(cx
->runtime
);
878 JS_LOCK_GC(cx
->runtime
);
882 requestDebit
= js_CountThreadRequests(cx
);
883 if (requestDebit
!= 0) {
884 JSRuntime
*rt
= cx
->runtime
;
885 JS_ASSERT(requestDebit
<= rt
->requestCount
);
886 rt
->requestCount
-= requestDebit
;
887 if (rt
->requestCount
== 0)
888 JS_NOTIFY_REQUEST_DONE(rt
);
894 js_RecountRequestsAfterGC(JSRuntime
*rt
, uint32 requestDebit
)
896 while (rt
->gcLevel
> 0) {
897 JS_ASSERT(rt
->gcThread
);
898 JS_AWAIT_GC_DONE(rt
);
900 if (requestDebit
!= 0)
901 rt
->requestCount
+= requestDebit
;
907 resolving_HashKey(JSDHashTable
*table
, const void *ptr
)
909 const JSResolvingKey
*key
= (const JSResolvingKey
*)ptr
;
911 return ((JSDHashNumber
)JS_PTR_TO_UINT32(key
->obj
) >> JSVAL_TAGBITS
) ^ key
->id
;
914 JS_PUBLIC_API(JSBool
)
915 resolving_MatchEntry(JSDHashTable
*table
,
916 const JSDHashEntryHdr
*hdr
,
919 const JSResolvingEntry
*entry
= (const JSResolvingEntry
*)hdr
;
920 const JSResolvingKey
*key
= (const JSResolvingKey
*)ptr
;
922 return entry
->key
.obj
== key
->obj
&& entry
->key
.id
== key
->id
;
925 static const JSDHashTableOps resolving_dhash_ops
= {
929 resolving_MatchEntry
,
930 JS_DHashMoveEntryStub
,
931 JS_DHashClearEntryStub
,
932 JS_DHashFinalizeStub
,
937 js_StartResolving(JSContext
*cx
, JSResolvingKey
*key
, uint32 flag
,
938 JSResolvingEntry
**entryp
)
941 JSResolvingEntry
*entry
;
943 table
= cx
->resolvingTable
;
945 table
= JS_NewDHashTable(&resolving_dhash_ops
, NULL
,
946 sizeof(JSResolvingEntry
),
950 cx
->resolvingTable
= table
;
953 entry
= (JSResolvingEntry
*)
954 JS_DHashTableOperate(table
, key
, JS_DHASH_ADD
);
958 if (entry
->flags
& flag
) {
959 /* An entry for (key, flag) exists already -- dampen recursion. */
962 /* Fill in key if we were the first to add entry, then set flag. */
965 entry
->flags
|= flag
;
971 JS_ReportOutOfMemory(cx
);
976 js_StopResolving(JSContext
*cx
, JSResolvingKey
*key
, uint32 flag
,
977 JSResolvingEntry
*entry
, uint32 generation
)
982 * Clear flag from entry->flags and return early if other flags remain.
983 * We must take care to re-lookup entry if the table has changed since
984 * it was found by js_StartResolving.
986 table
= cx
->resolvingTable
;
987 if (!entry
|| table
->generation
!= generation
) {
988 entry
= (JSResolvingEntry
*)
989 JS_DHashTableOperate(table
, key
, JS_DHASH_LOOKUP
);
991 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
));
992 entry
->flags
&= ~flag
;
997 * Do a raw remove only if fewer entries were removed than would cause
998 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
999 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
1000 * compressing or shrinking the table as needed.
1002 if (table
->removedCount
< JS_DHASH_TABLE_SIZE(table
) >> 2)
1003 JS_DHashTableRawRemove(table
, &entry
->hdr
);
1005 JS_DHashTableOperate(table
, key
, JS_DHASH_REMOVE
);
1009 js_EnterLocalRootScope(JSContext
*cx
)
1011 JSLocalRootStack
*lrs
;
1014 lrs
= cx
->localRootStack
;
1016 lrs
= (JSLocalRootStack
*) cx
->malloc(sizeof *lrs
);
1019 lrs
->scopeMark
= JSLRS_NULL_MARK
;
1021 lrs
->topChunk
= &lrs
->firstChunk
;
1022 lrs
->firstChunk
.down
= NULL
;
1023 cx
->localRootStack
= lrs
;
1026 /* Push lrs->scopeMark to save it for restore when leaving. */
1027 mark
= js_PushLocalRoot(cx
, lrs
, INT_TO_JSVAL(lrs
->scopeMark
));
1030 lrs
->scopeMark
= (uint32
) mark
;
1035 js_LeaveLocalRootScopeWithResult(JSContext
*cx
, jsval rval
)
1037 JSLocalRootStack
*lrs
;
1039 JSLocalRootChunk
*lrc
;
1041 /* Defend against buggy native callers. */
1042 lrs
= cx
->localRootStack
;
1043 JS_ASSERT(lrs
&& lrs
->rootCount
!= 0);
1044 if (!lrs
|| lrs
->rootCount
== 0)
1047 mark
= lrs
->scopeMark
;
1048 JS_ASSERT(mark
!= JSLRS_NULL_MARK
);
1049 if (mark
== JSLRS_NULL_MARK
)
1052 /* Free any chunks being popped by this leave operation. */
1053 m
= mark
>> JSLRS_CHUNK_SHIFT
;
1054 n
= (lrs
->rootCount
- 1) >> JSLRS_CHUNK_SHIFT
;
1056 lrc
= lrs
->topChunk
;
1057 JS_ASSERT(lrc
!= &lrs
->firstChunk
);
1058 lrs
->topChunk
= lrc
->down
;
1064 * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
1065 * it on the caller's scope, or store it in lastInternalResult if we are
1066 * leaving the outermost scope. We don't need to allocate a new lrc
1067 * because we can overwrite the old mark's slot with rval.
1069 lrc
= lrs
->topChunk
;
1070 m
= mark
& JSLRS_CHUNK_MASK
;
1071 lrs
->scopeMark
= (uint32
) JSVAL_TO_INT(lrc
->roots
[m
]);
1072 if (JSVAL_IS_GCTHING(rval
) && !JSVAL_IS_NULL(rval
)) {
1074 cx
->weakRoots
.lastInternalResult
= rval
;
1077 * Increment m to avoid the "else if (m == 0)" case below. If
1078 * rval is not a GC-thing, that case would take care of freeing
1079 * any chunk that contained only the old mark. Since rval *is*
1080 * a GC-thing here, we want to reuse that old mark's slot.
1082 lrc
->roots
[m
++] = rval
;
1086 lrs
->rootCount
= (uint32
) mark
;
1089 * Free the stack eagerly, risking malloc churn. The alternative would
1090 * require an lrs->entryCount member, maintained by Enter and Leave, and
1091 * tested by the GC in addition to the cx->localRootStack non-null test.
1093 * That approach would risk hoarding 264 bytes (net) per context. Right
1094 * now it seems better to give fresh (dirty in CPU write-back cache, and
1095 * the data is no longer needed) memory back to the malloc heap.
1098 cx
->localRootStack
= NULL
;
1100 } else if (m
== 0) {
1101 lrs
->topChunk
= lrc
->down
;
1107 js_ForgetLocalRoot(JSContext
*cx
, jsval v
)
1109 JSLocalRootStack
*lrs
;
1110 uint32 i
, j
, m
, n
, mark
;
1111 JSLocalRootChunk
*lrc
, *lrc2
;
1114 lrs
= cx
->localRootStack
;
1115 JS_ASSERT(lrs
&& lrs
->rootCount
);
1116 if (!lrs
|| lrs
->rootCount
== 0)
1119 /* Prepare to pop the top-most value from the stack. */
1120 n
= lrs
->rootCount
- 1;
1121 m
= n
& JSLRS_CHUNK_MASK
;
1122 lrc
= lrs
->topChunk
;
1123 top
= lrc
->roots
[m
];
1125 /* Be paranoid about calls on an empty scope. */
1126 mark
= lrs
->scopeMark
;
1127 JS_ASSERT(mark
< n
);
1131 /* If v was not the last root pushed in the top scope, find it. */
1133 /* Search downward in case v was recently pushed. */
1137 while (--i
> mark
) {
1140 j
= i
& JSLRS_CHUNK_MASK
;
1141 if (lrc2
->roots
[j
] == v
)
1145 /* If we didn't find v in this scope, assert and bail out. */
1146 JS_ASSERT(i
!= mark
);
1150 /* Swap top and v so common tail code can pop v. */
1151 lrc2
->roots
[j
] = top
;
1154 /* Pop the last value from the stack. */
1155 lrc
->roots
[m
] = JSVAL_NULL
;
1159 JS_ASSERT(lrc
!= &lrs
->firstChunk
);
1160 lrs
->topChunk
= lrc
->down
;
1166 js_PushLocalRoot(JSContext
*cx
, JSLocalRootStack
*lrs
, jsval v
)
1169 JSLocalRootChunk
*lrc
;
1172 m
= n
& JSLRS_CHUNK_MASK
;
1173 if (n
== 0 || m
!= 0) {
1175 * At start of first chunk, or not at start of a non-first top chunk.
1176 * Check for lrs->rootCount overflow.
1178 if ((uint32
)(n
+ 1) == 0) {
1179 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1180 JSMSG_TOO_MANY_LOCAL_ROOTS
);
1183 lrc
= lrs
->topChunk
;
1184 JS_ASSERT(n
!= 0 || lrc
== &lrs
->firstChunk
);
1187 * After lrs->firstChunk, trying to index at a power-of-two chunk
1188 * boundary: need a new chunk.
1190 lrc
= (JSLocalRootChunk
*) cx
->malloc(sizeof *lrc
);
1193 lrc
->down
= lrs
->topChunk
;
1194 lrs
->topChunk
= lrc
;
1196 lrs
->rootCount
= n
+ 1;
1202 js_TraceLocalRoots(JSTracer
*trc
, JSLocalRootStack
*lrs
)
1205 JSLocalRootChunk
*lrc
;
1212 mark
= lrs
->scopeMark
;
1213 lrc
= lrs
->topChunk
;
1215 while (--n
> mark
) {
1216 m
= n
& JSLRS_CHUNK_MASK
;
1218 JS_ASSERT(JSVAL_IS_GCTHING(v
) && v
!= JSVAL_NULL
);
1219 JS_SET_TRACING_INDEX(trc
, "local_root", n
);
1220 js_CallValueTracerIfGCThing(trc
, v
);
1224 m
= n
& JSLRS_CHUNK_MASK
;
1225 mark
= JSVAL_TO_INT(lrc
->roots
[m
]);
1233 ReportError(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
1236 * Check the error report, and set a JavaScript-catchable exception
1237 * if the error is defined to have an associated exception. If an
1238 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
1239 * on the error report, and exception-aware hosts should ignore it.
1242 if (reportp
->errorNumber
== JSMSG_UNCAUGHT_EXCEPTION
)
1243 reportp
->flags
|= JSREPORT_EXCEPTION
;
1246 * Call the error reporter only if an exception wasn't raised.
1248 * If an exception was raised, then we call the debugErrorHook
1249 * (if present) to give it a chance to see the error before it
1250 * propagates out of scope. This is needed for compatability
1251 * with the old scheme.
1253 if (!JS_IsRunning(cx
) || !js_ErrorToException(cx
, message
, reportp
)) {
1254 js_ReportErrorAgain(cx
, message
, reportp
);
1255 } else if (cx
->debugHooks
->debugErrorHook
&& cx
->errorReporter
) {
1256 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
1257 /* test local in case debugErrorHook changed on another thread */
1259 hook(cx
, message
, reportp
, cx
->debugHooks
->debugErrorHookData
);
1263 /* The report must be initially zeroed. */
1265 PopulateReportBlame(JSContext
*cx
, JSErrorReport
*report
)
1270 * Walk stack until we find a frame that is associated with some script
1271 * rather than a native frame.
1273 for (fp
= js_GetTopStackFrame(cx
); fp
; fp
= fp
->down
) {
1275 report
->filename
= fp
->script
->filename
;
1276 report
->lineno
= js_FramePCToLineNumber(cx
, fp
);
1283 * We don't post an exception in this case, since doing so runs into
1284 * complications of pre-allocating an exception object which required
1285 * running the Exception class initializer early etc.
1286 * Instead we just invoke the errorReporter with an "Out Of Memory"
1287 * type message, and then hope the process ends swiftly.
1290 js_ReportOutOfMemory(JSContext
*cx
)
1294 * If we are in a builtin called directly from trace, don't report an
1295 * error. We will retry in the interpreter instead.
1297 if (JS_ON_TRACE(cx
) && !cx
->bailExit
)
1301 JSErrorReport report
;
1302 JSErrorReporter onError
= cx
->errorReporter
;
1304 /* Get the message for this error, but we won't expand any arguments. */
1305 const JSErrorFormatString
*efs
=
1306 js_GetLocalizedErrorMessage(cx
, NULL
, NULL
, JSMSG_OUT_OF_MEMORY
);
1307 const char *msg
= efs
? efs
->format
: "Out of memory";
1309 /* Fill out the report, but don't do anything that requires allocation. */
1310 memset(&report
, 0, sizeof (struct JSErrorReport
));
1311 report
.flags
= JSREPORT_ERROR
;
1312 report
.errorNumber
= JSMSG_OUT_OF_MEMORY
;
1313 PopulateReportBlame(cx
, &report
);
1316 * If debugErrorHook is present then we give it a chance to veto sending
1317 * the error on to the regular ErrorReporter. We also clear a pending
1318 * exception if any now so the hooks can replace the out-of-memory error
1319 * by a script-catchable exception.
1321 cx
->throwing
= JS_FALSE
;
1323 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
1325 !hook(cx
, msg
, &report
, cx
->debugHooks
->debugErrorHookData
)) {
1331 onError(cx
, msg
, &report
);
1335 js_ReportOutOfScriptQuota(JSContext
*cx
)
1337 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1338 JSMSG_SCRIPT_STACK_QUOTA
);
1342 js_ReportOverRecursed(JSContext
*cx
)
1344 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_OVER_RECURSED
);
1348 js_ReportAllocationOverflow(JSContext
*cx
)
1350 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_ALLOC_OVERFLOW
);
1354 js_ReportErrorVA(JSContext
*cx
, uintN flags
, const char *format
, va_list ap
)
1359 JSErrorReport report
;
1362 if ((flags
& JSREPORT_STRICT
) && !JS_HAS_STRICT_OPTION(cx
))
1365 message
= JS_vsmprintf(format
, ap
);
1368 messagelen
= strlen(message
);
1370 memset(&report
, 0, sizeof (struct JSErrorReport
));
1371 report
.flags
= flags
;
1372 report
.errorNumber
= JSMSG_USER_DEFINED_ERROR
;
1373 report
.ucmessage
= ucmessage
= js_InflateString(cx
, message
, &messagelen
);
1374 PopulateReportBlame(cx
, &report
);
1376 warning
= JSREPORT_IS_WARNING(report
.flags
);
1377 if (warning
&& JS_HAS_WERROR_OPTION(cx
)) {
1378 report
.flags
&= ~JSREPORT_WARNING
;
1382 ReportError(cx
, message
, &report
);
1384 cx
->free(ucmessage
);
1389 * The arguments from ap need to be packaged up into an array and stored
1390 * into the report struct.
1392 * The format string addressed by the error number may contain operands
1393 * identified by the format {N}, where N is a decimal digit. Each of these
1394 * is to be replaced by the Nth argument from the va_list. The complete
1395 * message is placed into reportp->ucmessage converted to a JSString.
1397 * Returns true if the expansion succeeds (can fail if out of memory).
1400 js_ExpandErrorArguments(JSContext
*cx
, JSErrorCallback callback
,
1401 void *userRef
, const uintN errorNumber
,
1402 char **messagep
, JSErrorReport
*reportp
,
1403 JSBool
*warningp
, JSBool charArgs
, va_list ap
)
1405 const JSErrorFormatString
*efs
;
1409 *warningp
= JSREPORT_IS_WARNING(reportp
->flags
);
1410 if (*warningp
&& JS_HAS_WERROR_OPTION(cx
)) {
1411 reportp
->flags
&= ~JSREPORT_WARNING
;
1412 *warningp
= JS_FALSE
;
1417 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1418 if (!callback
|| callback
== js_GetErrorMessage
)
1419 efs
= js_GetLocalizedErrorMessage(cx
, userRef
, NULL
, errorNumber
);
1421 efs
= callback(userRef
, NULL
, errorNumber
);
1423 size_t totalArgsLength
= 0;
1424 size_t argLengths
[10]; /* only {0} thru {9} supported */
1425 argCount
= efs
->argCount
;
1426 JS_ASSERT(argCount
<= 10);
1429 * Gather the arguments into an array, and accumulate
1430 * their sizes. We allocate 1 more than necessary and
1431 * null it out to act as the caboose when we free the
1434 reportp
->messageArgs
= (const jschar
**)
1435 cx
->malloc(sizeof(jschar
*) * (argCount
+ 1));
1436 if (!reportp
->messageArgs
)
1438 reportp
->messageArgs
[argCount
] = NULL
;
1439 for (i
= 0; i
< argCount
; i
++) {
1441 char *charArg
= va_arg(ap
, char *);
1442 size_t charArgLength
= strlen(charArg
);
1443 reportp
->messageArgs
[i
]
1444 = js_InflateString(cx
, charArg
, &charArgLength
);
1445 if (!reportp
->messageArgs
[i
])
1448 reportp
->messageArgs
[i
] = va_arg(ap
, jschar
*);
1450 argLengths
[i
] = js_strlen(reportp
->messageArgs
[i
]);
1451 totalArgsLength
+= argLengths
[i
];
1453 /* NULL-terminate for easy copying. */
1454 reportp
->messageArgs
[i
] = NULL
;
1457 * Parse the error format, substituting the argument X
1458 * for {X} in the format.
1462 jschar
*buffer
, *fmt
, *out
;
1463 int expandedArgs
= 0;
1464 size_t expandedLength
;
1465 size_t len
= strlen(efs
->format
);
1467 buffer
= fmt
= js_InflateString (cx
, efs
->format
, &len
);
1470 expandedLength
= len
1471 - (3 * argCount
) /* exclude the {n} */
1475 * Note - the above calculation assumes that each argument
1476 * is used once and only once in the expansion !!!
1478 reportp
->ucmessage
= out
= (jschar
*)
1479 cx
->malloc((expandedLength
+ 1) * sizeof(jschar
));
1486 if (isdigit(fmt
[1])) {
1487 int d
= JS7_UNDEC(fmt
[1]);
1488 JS_ASSERT(d
< argCount
);
1489 js_strncpy(out
, reportp
->messageArgs
[d
],
1491 out
+= argLengths
[d
];
1499 JS_ASSERT(expandedArgs
== argCount
);
1503 js_DeflateString(cx
, reportp
->ucmessage
,
1504 (size_t)(out
- reportp
->ucmessage
));
1510 * Zero arguments: the format string (if it exists) is the
1515 *messagep
= JS_strdup(cx
, efs
->format
);
1518 len
= strlen(*messagep
);
1519 reportp
->ucmessage
= js_InflateString(cx
, *messagep
, &len
);
1520 if (!reportp
->ucmessage
)
1525 if (*messagep
== NULL
) {
1526 /* where's the right place for this ??? */
1527 const char *defaultErrorMessage
1528 = "No error message available for error number %d";
1529 size_t nbytes
= strlen(defaultErrorMessage
) + 16;
1530 *messagep
= (char *)cx
->malloc(nbytes
);
1533 JS_snprintf(*messagep
, nbytes
, defaultErrorMessage
, errorNumber
);
1538 if (reportp
->messageArgs
) {
1539 /* free the arguments only if we allocated them */
1542 while (reportp
->messageArgs
[i
])
1543 cx
->free((void *)reportp
->messageArgs
[i
++]);
1545 cx
->free((void *)reportp
->messageArgs
);
1546 reportp
->messageArgs
= NULL
;
1548 if (reportp
->ucmessage
) {
1549 cx
->free((void *)reportp
->ucmessage
);
1550 reportp
->ucmessage
= NULL
;
1553 cx
->free((void *)*messagep
);
1560 js_ReportErrorNumberVA(JSContext
*cx
, uintN flags
, JSErrorCallback callback
,
1561 void *userRef
, const uintN errorNumber
,
1562 JSBool charArgs
, va_list ap
)
1564 JSErrorReport report
;
1568 if ((flags
& JSREPORT_STRICT
) && !JS_HAS_STRICT_OPTION(cx
))
1571 memset(&report
, 0, sizeof (struct JSErrorReport
));
1572 report
.flags
= flags
;
1573 report
.errorNumber
= errorNumber
;
1574 PopulateReportBlame(cx
, &report
);
1576 if (!js_ExpandErrorArguments(cx
, callback
, userRef
, errorNumber
,
1577 &message
, &report
, &warning
, charArgs
, ap
)) {
1581 ReportError(cx
, message
, &report
);
1585 if (report
.messageArgs
) {
1587 * js_ExpandErrorArguments owns its messageArgs only if it had to
1588 * inflate the arguments (from regular |char *|s).
1592 while (report
.messageArgs
[i
])
1593 cx
->free((void *)report
.messageArgs
[i
++]);
1595 cx
->free((void *)report
.messageArgs
);
1597 if (report
.ucmessage
)
1598 cx
->free((void *)report
.ucmessage
);
1604 js_ReportErrorAgain(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
1606 JSErrorReporter onError
;
1611 if (cx
->lastMessage
)
1612 js_free(cx
->lastMessage
);
1613 cx
->lastMessage
= JS_strdup(cx
, message
);
1614 if (!cx
->lastMessage
)
1616 onError
= cx
->errorReporter
;
1619 * If debugErrorHook is present then we give it a chance to veto
1620 * sending the error on to the regular ErrorReporter.
1623 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
1625 !hook(cx
, cx
->lastMessage
, reportp
,
1626 cx
->debugHooks
->debugErrorHookData
)) {
1631 onError(cx
, cx
->lastMessage
, reportp
);
1635 js_ReportIsNotDefined(JSContext
*cx
, const char *name
)
1637 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_DEFINED
, name
);
1641 js_ReportIsNullOrUndefined(JSContext
*cx
, intN spindex
, jsval v
,
1647 bytes
= js_DecompileValueGenerator(cx
, spindex
, v
, fallback
);
1651 if (strcmp(bytes
, js_undefined_str
) == 0 ||
1652 strcmp(bytes
, js_null_str
) == 0) {
1653 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1654 js_GetErrorMessage
, NULL
,
1655 JSMSG_NO_PROPERTIES
, bytes
,
1657 } else if (JSVAL_IS_VOID(v
)) {
1658 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1659 js_GetErrorMessage
, NULL
,
1660 JSMSG_NULL_OR_UNDEFINED
, bytes
,
1661 js_undefined_str
, NULL
);
1663 JS_ASSERT(JSVAL_IS_NULL(v
));
1664 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1665 js_GetErrorMessage
, NULL
,
1666 JSMSG_NULL_OR_UNDEFINED
, bytes
,
1675 js_ReportMissingArg(JSContext
*cx
, jsval
*vp
, uintN arg
)
1681 JS_snprintf(argbuf
, sizeof argbuf
, "%u", arg
);
1683 if (VALUE_IS_FUNCTION(cx
, *vp
)) {
1684 atom
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(*vp
))->atom
;
1685 bytes
= js_DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, *vp
,
1686 ATOM_TO_STRING(atom
));
1690 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1691 JSMSG_MISSING_FUN_ARG
, argbuf
,
1692 bytes
? bytes
: "");
1697 js_ReportValueErrorFlags(JSContext
*cx
, uintN flags
, const uintN errorNumber
,
1698 intN spindex
, jsval v
, JSString
*fallback
,
1699 const char *arg1
, const char *arg2
)
1704 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
>= 1);
1705 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
<= 3);
1706 bytes
= js_DecompileValueGenerator(cx
, spindex
, v
, fallback
);
1710 ok
= JS_ReportErrorFlagsAndNumber(cx
, flags
, js_GetErrorMessage
,
1711 NULL
, errorNumber
, bytes
, arg1
, arg2
);
1716 #if defined DEBUG && defined XP_UNIX
1717 /* For gdb usage. */
1718 void js_traceon(JSContext
*cx
) { cx
->tracefp
= stderr
; cx
->tracePrevPc
= NULL
; }
1719 void js_traceoff(JSContext
*cx
) { cx
->tracefp
= NULL
; }
1722 JSErrorFormatString js_ErrorFormatString
[JSErr_Limit
] = {
1723 #define MSG_DEF(name, number, count, exception, format) \
1724 { format, count, exception } ,
1729 JS_FRIEND_API(const JSErrorFormatString
*)
1730 js_GetErrorMessage(void *userRef
, const char *locale
, const uintN errorNumber
)
1732 if ((errorNumber
> 0) && (errorNumber
< JSErr_Limit
))
1733 return &js_ErrorFormatString
[errorNumber
];
1738 js_InvokeOperationCallback(JSContext
*cx
)
1740 JS_ASSERT(cx
->operationCallbackFlag
);
1743 * Reset the callback flag first, then yield. If another thread is racing
1744 * us here we will accumulate another callback request which will be
1745 * serviced at the next opportunity.
1747 cx
->operationCallbackFlag
= 0;
1750 * Unless we are going to run the GC, we automatically yield the current
1751 * context every time the operation callback is hit since we might be
1752 * called as a result of an impending GC, which would deadlock if we do
1753 * not yield. Operation callbacks are supposed to happen rarely (seconds,
1754 * not milliseconds) so it is acceptable to yield at every callback.
1756 if (cx
->runtime
->gcIsNeeded
)
1757 js_GC(cx
, GC_NORMAL
);
1758 #ifdef JS_THREADSAFE
1760 JS_YieldRequest(cx
);
1763 JSOperationCallback cb
= cx
->operationCallback
;
1766 * Important: Additional callbacks can occur inside the callback handler
1767 * if it re-enters the JS engine. The embedding must ensure that the
1768 * callback is disconnected before attempting such re-entry.
1771 return !cb
|| cb(cx
);
1775 js_TriggerAllOperationCallbacks(JSRuntime
*rt
, JSBool gcLocked
)
1777 JSContext
*acx
, *iter
;
1778 #ifdef JS_THREADSAFE
1783 while ((acx
= js_ContextIterator(rt
, JS_FALSE
, &iter
)))
1784 JS_TriggerOperationCallback(acx
);
1785 #ifdef JS_THREADSAFE
1792 js_GetScriptedCaller(JSContext
*cx
, JSStackFrame
*fp
)
1795 fp
= js_GetTopStackFrame(cx
);
1805 js_GetCurrentBytecodePC(JSContext
* cx
)
1807 jsbytecode
*pc
, *imacpc
;
1810 if (JS_ON_TRACE(cx
)) {
1811 pc
= cx
->bailExit
->pc
;
1812 imacpc
= cx
->bailExit
->imacpc
;
1816 JS_ASSERT_NOT_ON_TRACE(cx
); /* for static analysis */
1817 JSStackFrame
* fp
= cx
->fp
;
1818 if (fp
&& fp
->regs
) {
1820 imacpc
= fp
->imacpc
;
1827 * If we are inside GetProperty_tn or similar, return a pointer to the
1828 * current instruction in the script, not the CALL instruction in the
1829 * imacro, for the benefit of callers doing bytecode inspection.
1831 return (*pc
== JSOP_CALL
&& imacpc
) ? imacpc
: pc
;