1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* vim: set ts=8 sw=4 et tw=80: |
* vim: set ts=8 sw=4 et tw=80: |
3 |
* |
* |
4 |
* ***** BEGIN LICENSE BLOCK ***** |
* ***** BEGIN LICENSE BLOCK ***** |
61 |
#include "jsnum.h" |
#include "jsnum.h" |
62 |
#include "jsobj.h" |
#include "jsobj.h" |
63 |
#include "jsopcode.h" |
#include "jsopcode.h" |
64 |
|
#include "jspubtd.h" |
65 |
#include "jsscan.h" |
#include "jsscan.h" |
66 |
#include "jsscope.h" |
#include "jsscope.h" |
67 |
#include "jsscript.h" |
#include "jsscript.h" |
68 |
|
#include "jsstaticcheck.h" |
69 |
#include "jsstr.h" |
#include "jsstr.h" |
70 |
#include "jstracer.h" |
#include "jstracer.h" |
71 |
|
|
72 |
#ifdef JS_THREADSAFE |
static void |
73 |
#include "prtypes.h" |
FreeContext(JSContext *cx); |
|
|
|
|
/* |
|
|
* The index for JSThread info, returned by PR_NewThreadPrivateIndex. The |
|
|
* index value is visible and shared by all threads, but the data associated |
|
|
* with it is private to each thread. |
|
|
*/ |
|
|
static PRUintn threadTPIndex; |
|
|
static JSBool tpIndexInited = JS_FALSE; |
|
74 |
|
|
75 |
JS_BEGIN_EXTERN_C |
static void |
76 |
JSBool |
InitThreadData(JSThreadData *data) |
|
js_InitThreadPrivateIndex(void (*ptr)(void *)) |
|
77 |
{ |
{ |
78 |
PRStatus status; |
#ifdef DEBUG |
79 |
|
/* The data must be already zeroed. */ |
80 |
if (tpIndexInited) |
for (size_t i = 0; i != sizeof(*data); ++i) |
81 |
return JS_TRUE; |
JS_ASSERT(reinterpret_cast<uint8*>(data)[i] == 0); |
82 |
|
#endif |
83 |
|
#ifdef JS_TRACER |
84 |
|
js_InitJIT(&data->traceMonitor); |
85 |
|
#endif |
86 |
|
} |
87 |
|
|
88 |
status = PR_NewThreadPrivateIndex(&threadTPIndex, ptr); |
static void |
89 |
|
FinishThreadData(JSThreadData *data) |
90 |
|
{ |
91 |
|
#ifdef DEBUG |
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]); |
95 |
|
#endif |
96 |
|
|
97 |
if (status == PR_SUCCESS) |
js_FinishGSNCache(&data->gsnCache); |
98 |
tpIndexInited = JS_TRUE; |
js_FinishPropertyCache(&data->propertyCache); |
99 |
return status == PR_SUCCESS; |
#if defined JS_TRACER |
100 |
|
js_FinishJIT(&data->traceMonitor); |
101 |
|
#endif |
102 |
} |
} |
|
JS_END_EXTERN_C |
|
103 |
|
|
104 |
/* |
static void |
105 |
* Callback function to delete a JSThread info when the thread that owns it |
PurgeThreadData(JSContext *cx, JSThreadData *data) |
|
* is destroyed. |
|
|
*/ |
|
|
void |
|
|
js_ThreadDestructorCB(void *ptr) |
|
106 |
{ |
{ |
107 |
JSThread *thread = (JSThread *)ptr; |
# ifdef JS_TRACER |
108 |
|
JSTraceMonitor *tm = &data->traceMonitor; |
109 |
|
tm->reservedDoublePoolPtr = tm->reservedDoublePool; |
110 |
|
tm->needFlush = JS_TRUE; |
111 |
|
|
112 |
if (!thread) |
if (tm->recorder) |
113 |
return; |
tm->recorder->deepAbort(); |
114 |
|
|
115 |
/* |
/* |
116 |
* Check that this thread properly called either JS_DestroyContext or |
* We want to keep tm->reservedObjects after the GC. So, unless we are |
117 |
* JS_ClearContextThread on each JSContext it created or used. |
* shutting down, we don't purge them here and rather mark them during |
118 |
|
* the GC, see MarkReservedObjects in jsgc.cpp. |
119 |
*/ |
*/ |
120 |
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); |
if (cx->runtime->state == JSRTS_LANDING) |
121 |
GSN_CACHE_CLEAR(&thread->gsnCache); |
tm->reservedObjects = NULL; |
122 |
#if defined JS_TRACER |
# endif |
|
js_FinishJIT(&thread->traceMonitor); |
|
|
#endif |
|
|
free(thread); |
|
|
} |
|
123 |
|
|
124 |
/* |
/* Destroy eval'ed scripts. */ |
125 |
* Get current thread-local JSThread info, creating one if it doesn't exist. |
js_DestroyScriptsToGC(cx, data); |
|
* Each thread has a unique JSThread pointer. |
|
|
* |
|
|
* Since we are dealing with thread-local data, no lock is needed. |
|
|
* |
|
|
* Return a pointer to the thread local info, NULL if the system runs out |
|
|
* of memory, or it failed to set thread private data (neither case is very |
|
|
* likely; both are probably due to out-of-memory). It is up to the caller |
|
|
* to report an error, if possible. |
|
|
*/ |
|
|
JSThread * |
|
|
js_GetCurrentThread(JSRuntime *rt) |
|
|
{ |
|
|
JSThread *thread; |
|
126 |
|
|
127 |
thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex); |
js_PurgeGSNCache(&data->gsnCache); |
128 |
if (!thread) { |
js_PurgePropertyCache(cx, &data->propertyCache); |
129 |
thread = (JSThread *) malloc(sizeof(JSThread)); |
} |
|
if (!thread) |
|
|
return NULL; |
|
|
#ifdef DEBUG |
|
|
memset(thread, JS_FREE_PATTERN, sizeof(JSThread)); |
|
|
#endif |
|
|
if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) { |
|
|
free(thread); |
|
|
return NULL; |
|
|
} |
|
130 |
|
|
131 |
JS_INIT_CLIST(&thread->contextList); |
#ifdef JS_THREADSAFE |
|
thread->id = js_CurrentThreadId(); |
|
|
thread->gcMallocBytes = 0; |
|
|
#ifdef JS_TRACER |
|
|
memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor)); |
|
|
js_InitJIT(&thread->traceMonitor); |
|
|
#endif |
|
|
thread->scriptsToGC = NULL; |
|
132 |
|
|
133 |
/* |
static JSThread * |
134 |
* js_SetContextThread initializes the remaining fields as necessary. |
NewThread(jsword id) |
135 |
*/ |
{ |
136 |
} |
JS_ASSERT(js_CurrentThreadId() == id); |
137 |
|
JSThread *thread = (JSThread *) calloc(1, sizeof(JSThread)); |
138 |
|
if (!thread) |
139 |
|
return NULL; |
140 |
|
JS_INIT_CLIST(&thread->contextList); |
141 |
|
thread->id = id; |
142 |
|
InitThreadData(&thread->data); |
143 |
return thread; |
return thread; |
144 |
} |
} |
145 |
|
|
146 |
/* |
static void |
147 |
* Sets current thread as owning thread of a context by assigning the |
DestroyThread(JSThread *thread) |
|
* thread-private info to the context. If the current thread doesn't have |
|
|
* private JSThread info, create one. |
|
|
*/ |
|
|
JSBool |
|
|
js_SetContextThread(JSContext *cx) |
|
148 |
{ |
{ |
149 |
JSThread *thread = js_GetCurrentThread(cx->runtime); |
/* The thread must have zero contexts. */ |
150 |
|
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); |
151 |
|
JS_ASSERT(!thread->titleToShare); |
152 |
|
FinishThreadData(&thread->data); |
153 |
|
free(thread); |
154 |
|
} |
155 |
|
|
156 |
if (!thread) { |
JSBool |
157 |
JS_ReportOutOfMemory(cx); |
js_InitContextThread(JSContext *cx) |
158 |
return JS_FALSE; |
{ |
159 |
} |
JS_ASSERT(!cx->thread); |
160 |
|
jsword id = js_CurrentThreadId(); |
161 |
|
JSRuntime *rt = cx->runtime; |
162 |
|
JS_LOCK_GC(rt); |
163 |
|
|
164 |
/* |
/* |
165 |
* Clear caches on each transition from 0 to 1 context active on the |
* We must not race with a GC that accesses cx->thread for JSContext |
166 |
* current thread. See bug 425828. |
* instances on all threads, see bug 476934. |
167 |
*/ |
*/ |
168 |
if (JS_CLIST_IS_EMPTY(&thread->contextList)) { |
js_WaitForGC(rt); |
169 |
memset(&thread->gsnCache, 0, sizeof(thread->gsnCache)); |
JSThreadsHashEntry *entry = (JSThreadsHashEntry *) |
170 |
memset(&thread->propertyCache, 0, sizeof(thread->propertyCache)); |
JS_DHashTableOperate(&rt->threads, |
171 |
|
(const void *) id, |
172 |
|
JS_DHASH_LOOKUP); |
173 |
|
JSThread *thread; |
174 |
|
if (JS_DHASH_ENTRY_IS_BUSY(&entry->base)) { |
175 |
|
thread = entry->thread; |
176 |
|
JS_ASSERT(thread->id == id); |
177 |
|
} else { |
178 |
|
JS_UNLOCK_GC(rt); |
179 |
|
thread = NewThread(id); |
180 |
|
if (!thread) |
181 |
|
return false; |
182 |
|
JS_LOCK_GC(rt); |
183 |
|
js_WaitForGC(rt); |
184 |
|
entry = (JSThreadsHashEntry *) |
185 |
|
JS_DHashTableOperate(&rt->threads, (const void *) id, |
186 |
|
JS_DHASH_ADD); |
187 |
|
if (!entry) { |
188 |
|
JS_UNLOCK_GC(rt); |
189 |
|
DestroyThread(thread); |
190 |
|
return false; |
191 |
|
} |
192 |
|
|
193 |
|
/* Another thread cannot initialize entry->thread. */ |
194 |
|
JS_ASSERT(!entry->thread); |
195 |
|
entry->thread = thread; |
196 |
} |
} |
197 |
|
|
198 |
/* Assert that the previous cx->thread called JS_ClearContextThread(). */ |
JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); |
|
JS_ASSERT(!cx->thread || cx->thread == thread); |
|
|
if (!cx->thread) |
|
|
JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); |
|
199 |
cx->thread = thread; |
cx->thread = thread; |
200 |
return JS_TRUE; |
return true; |
201 |
} |
} |
202 |
|
|
|
/* Remove the owning thread info of a context. */ |
|
203 |
void |
void |
204 |
js_ClearContextThread(JSContext *cx) |
js_ClearContextThread(JSContext *cx) |
205 |
{ |
{ |
206 |
/* |
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); |
|
* If cx is associated with a thread, this must be called only from that |
|
|
* thread. If not, this is a harmless no-op. |
|
|
*/ |
|
|
JS_ASSERT(cx->thread == js_GetCurrentThread(cx->runtime) || !cx->thread); |
|
207 |
JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); |
JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); |
208 |
cx->thread = NULL; |
cx->thread = NULL; |
209 |
} |
} |
210 |
|
|
211 |
|
static JSBool |
212 |
|
thread_matchEntry(JSDHashTable *table, |
213 |
|
const JSDHashEntryHdr *hdr, |
214 |
|
const void *key) |
215 |
|
{ |
216 |
|
const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr; |
217 |
|
|
218 |
|
return entry->thread->id == (jsword) key; |
219 |
|
} |
220 |
|
|
221 |
|
static const JSDHashTableOps threads_ops = { |
222 |
|
JS_DHashAllocTable, |
223 |
|
JS_DHashFreeTable, |
224 |
|
JS_DHashVoidPtrKeyStub, |
225 |
|
thread_matchEntry, |
226 |
|
JS_DHashMoveEntryStub, |
227 |
|
JS_DHashClearEntryStub, |
228 |
|
JS_DHashFinalizeStub, |
229 |
|
NULL |
230 |
|
}; |
231 |
|
|
232 |
|
static JSDHashOperator |
233 |
|
thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, |
234 |
|
void * /* arg */) |
235 |
|
{ |
236 |
|
JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr; |
237 |
|
JSThread *thread = entry->thread; |
238 |
|
|
239 |
|
JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); |
240 |
|
DestroyThread(thread); |
241 |
|
return JS_DHASH_REMOVE; |
242 |
|
} |
243 |
|
|
244 |
|
static JSDHashOperator |
245 |
|
thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, |
246 |
|
void *arg) |
247 |
|
{ |
248 |
|
JSContext* cx = (JSContext *) arg; |
249 |
|
JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread; |
250 |
|
|
251 |
|
if (JS_CLIST_IS_EMPTY(&thread->contextList)) { |
252 |
|
JS_ASSERT(cx->thread != thread); |
253 |
|
js_DestroyScriptsToGC(cx, &thread->data); |
254 |
|
DestroyThread(thread); |
255 |
|
return JS_DHASH_REMOVE; |
256 |
|
} |
257 |
|
PurgeThreadData(cx, &thread->data); |
258 |
|
return JS_DHASH_NEXT; |
259 |
|
} |
260 |
|
|
261 |
#endif /* JS_THREADSAFE */ |
#endif /* JS_THREADSAFE */ |
262 |
|
|
263 |
|
JSBool |
264 |
|
js_InitThreads(JSRuntime *rt) |
265 |
|
{ |
266 |
|
#ifdef JS_THREADSAFE |
267 |
|
if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL, |
268 |
|
sizeof(JSThreadsHashEntry), 4)) { |
269 |
|
rt->threads.ops = NULL; |
270 |
|
return false; |
271 |
|
} |
272 |
|
#else |
273 |
|
InitThreadData(&rt->threadData); |
274 |
|
#endif |
275 |
|
return true; |
276 |
|
} |
277 |
|
|
278 |
|
void |
279 |
|
js_FinishThreads(JSRuntime *rt) |
280 |
|
{ |
281 |
|
#ifdef JS_THREADSAFE |
282 |
|
if (!rt->threads.ops) |
283 |
|
return; |
284 |
|
JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL); |
285 |
|
JS_DHashTableFinish(&rt->threads); |
286 |
|
rt->threads.ops = NULL; |
287 |
|
#else |
288 |
|
FinishThreadData(&rt->threadData); |
289 |
|
#endif |
290 |
|
} |
291 |
|
|
292 |
|
void |
293 |
|
js_PurgeThreads(JSContext *cx) |
294 |
|
{ |
295 |
|
#ifdef JS_THREADSAFE |
296 |
|
JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx); |
297 |
|
#else |
298 |
|
PurgeThreadData(cx, &cx->runtime->threadData); |
299 |
|
#endif |
300 |
|
} |
301 |
|
|
302 |
|
/* |
303 |
|
* JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version |
304 |
|
* associated with scripts, so in addition to storing them in cx->options we |
305 |
|
* duplicate them in cx->version (script->version, etc.) and ensure each bit |
306 |
|
* remains synchronized between the two through these two functions. |
307 |
|
*/ |
308 |
|
void |
309 |
|
js_SyncOptionsToVersion(JSContext* cx) |
310 |
|
{ |
311 |
|
if (cx->options & JSOPTION_XML) |
312 |
|
cx->version |= JSVERSION_HAS_XML; |
313 |
|
else |
314 |
|
cx->version &= ~JSVERSION_HAS_XML; |
315 |
|
if (cx->options & JSOPTION_ANONFUNFIX) |
316 |
|
cx->version |= JSVERSION_ANONFUNFIX; |
317 |
|
else |
318 |
|
cx->version &= ~JSVERSION_ANONFUNFIX; |
319 |
|
} |
320 |
|
|
321 |
|
inline void |
322 |
|
js_SyncVersionToOptions(JSContext* cx) |
323 |
|
{ |
324 |
|
if (cx->version & JSVERSION_HAS_XML) |
325 |
|
cx->options |= JSOPTION_XML; |
326 |
|
else |
327 |
|
cx->options &= ~JSOPTION_XML; |
328 |
|
if (cx->version & JSVERSION_ANONFUNFIX) |
329 |
|
cx->options |= JSOPTION_ANONFUNFIX; |
330 |
|
else |
331 |
|
cx->options &= ~JSOPTION_ANONFUNFIX; |
332 |
|
} |
333 |
|
|
334 |
void |
void |
335 |
js_OnVersionChange(JSContext *cx) |
js_OnVersionChange(JSContext *cx) |
336 |
{ |
{ |
345 |
js_SetVersion(JSContext *cx, JSVersion version) |
js_SetVersion(JSContext *cx, JSVersion version) |
346 |
{ |
{ |
347 |
cx->version = version; |
cx->version = version; |
348 |
|
js_SyncVersionToOptions(cx); |
349 |
js_OnVersionChange(cx); |
js_OnVersionChange(cx); |
350 |
} |
} |
351 |
|
|
356 |
JSBool ok, first; |
JSBool ok, first; |
357 |
JSContextCallback cxCallback; |
JSContextCallback cxCallback; |
358 |
|
|
359 |
cx = (JSContext *) malloc(sizeof *cx); |
/* |
360 |
|
* We need to initialize the new context fully before adding it to the |
361 |
|
* runtime list. After that it can be accessed from another thread via |
362 |
|
* js_ContextIterator. |
363 |
|
*/ |
364 |
|
cx = (JSContext *) calloc(1, sizeof *cx); |
365 |
if (!cx) |
if (!cx) |
366 |
return NULL; |
return NULL; |
|
memset(cx, 0, sizeof *cx); |
|
367 |
|
|
368 |
cx->runtime = rt; |
cx->runtime = rt; |
|
JS_ClearOperationCallback(cx); |
|
369 |
cx->debugHooks = &rt->globalDebugHooks; |
cx->debugHooks = &rt->globalDebugHooks; |
370 |
#if JS_STACK_GROWTH_DIRECTION > 0 |
#if JS_STACK_GROWTH_DIRECTION > 0 |
371 |
cx->stackLimit = (jsuword)-1; |
cx->stackLimit = (jsuword) -1; |
372 |
#endif |
#endif |
373 |
cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; |
cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; |
374 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
375 |
cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet; |
cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet; |
|
JS_INIT_CLIST(&cx->threadLinks); |
|
|
js_SetContextThread(cx); |
|
376 |
#endif |
#endif |
377 |
|
JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0); |
378 |
|
JS_ASSERT(cx->version == JSVERSION_DEFAULT); |
379 |
|
VOUCH_DOES_NOT_REQUIRE_STACK(); |
380 |
|
JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval), |
381 |
|
&cx->scriptStackQuota); |
382 |
|
|
383 |
JS_LOCK_GC(rt); |
JS_INIT_ARENA_POOL(&cx->tempPool, "temp", |
384 |
|
1024, /* FIXME: bug 421435 */ |
385 |
|
sizeof(jsdouble), &cx->scriptStackQuota); |
386 |
|
|
387 |
|
js_InitRegExpStatics(cx); |
388 |
|
JS_ASSERT(cx->resolveFlags == 0); |
389 |
|
|
390 |
|
#ifdef JS_THREADSAFE |
391 |
|
if (!js_InitContextThread(cx)) { |
392 |
|
FreeContext(cx); |
393 |
|
return NULL; |
394 |
|
} |
395 |
|
#endif |
396 |
|
|
397 |
|
/* |
398 |
|
* Here the GC lock is still held after js_InitContextThread took it and |
399 |
|
* the GC is not running on another thread. |
400 |
|
*/ |
401 |
for (;;) { |
for (;;) { |
|
first = (rt->contextList.next == &rt->contextList); |
|
402 |
if (rt->state == JSRTS_UP) { |
if (rt->state == JSRTS_UP) { |
403 |
JS_ASSERT(!first); |
JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList)); |
404 |
|
first = JS_FALSE; |
405 |
break; |
break; |
406 |
} |
} |
407 |
if (rt->state == JSRTS_DOWN) { |
if (rt->state == JSRTS_DOWN) { |
408 |
JS_ASSERT(first); |
JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList)); |
409 |
|
first = JS_TRUE; |
410 |
rt->state = JSRTS_LAUNCHING; |
rt->state = JSRTS_LAUNCHING; |
411 |
break; |
break; |
412 |
} |
} |
413 |
JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); |
JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); |
|
} |
|
|
JS_APPEND_LINK(&cx->links, &rt->contextList); |
|
|
JS_UNLOCK_GC(rt); |
|
|
|
|
|
/* |
|
|
* First we do the infallible, every-time per-context initializations. |
|
|
* Should a later, fallible initialization (js_InitRegExpStatics, e.g., |
|
|
* or the stuff under 'if (first)' below) fail, at least the version |
|
|
* and arena-pools will be valid and safe to use (say, from the last GC |
|
|
* done by js_DestroyContext). |
|
|
*/ |
|
|
cx->version = JSVERSION_DEFAULT; |
|
|
JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval), |
|
|
&cx->scriptStackQuota); |
|
414 |
|
|
415 |
JS_INIT_ARENA_POOL(&cx->tempPool, "temp", |
/* |
416 |
1024, /* FIXME: bug 421435 */ |
* During the above wait after we are notified about the state change |
417 |
sizeof(jsdouble), &cx->scriptStackQuota); |
* but before we wake up, another thread could enter the GC from |
418 |
|
* js_DestroyContext, bug 478336. So we must wait here to ensure that |
419 |
/* |
* when we exit the loop with the first flag set to true, that GC is |
420 |
* To avoid multiple allocations in InitMatch() (in jsregexp.c), the arena |
* finished. |
421 |
* size parameter should be at least as big as: |
*/ |
422 |
* INITIAL_BACKTRACK |
js_WaitForGC(rt); |
|
* + (sizeof(REProgState) * INITIAL_STATESTACK) |
|
|
* + (offsetof(REMatchState, parens) + avgParanSize * sizeof(RECapture)) |
|
|
*/ |
|
|
JS_INIT_ARENA_POOL(&cx->regexpPool, "regexp", |
|
|
12 * 1024 - 40, /* FIXME: bug 421435 */ |
|
|
sizeof(void *), &cx->scriptStackQuota); |
|
|
|
|
|
if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) { |
|
|
js_DestroyContext(cx, JSDCM_NEW_FAILED); |
|
|
return NULL; |
|
423 |
} |
} |
424 |
|
JS_APPEND_LINK(&cx->link, &rt->contextList); |
425 |
cx->resolveFlags = 0; |
JS_UNLOCK_GC(rt); |
426 |
|
|
427 |
/* |
/* |
428 |
* If cx is the first context on this runtime, initialize well-known atoms, |
* If cx is the first context on this runtime, initialize well-known atoms, |
472 |
return cx; |
return cx; |
473 |
} |
} |
474 |
|
|
475 |
|
#if defined DEBUG && defined XP_UNIX |
476 |
|
# include <stdio.h> |
477 |
|
|
478 |
|
class JSAutoFile { |
479 |
|
public: |
480 |
|
JSAutoFile() : mFile(NULL) {} |
481 |
|
|
482 |
|
~JSAutoFile() { |
483 |
|
if (mFile) |
484 |
|
fclose(mFile); |
485 |
|
} |
486 |
|
|
487 |
|
FILE *open(const char *fname, const char *mode) { |
488 |
|
return mFile = fopen(fname, mode); |
489 |
|
} |
490 |
|
operator FILE *() { |
491 |
|
return mFile; |
492 |
|
} |
493 |
|
|
494 |
|
private: |
495 |
|
FILE *mFile; |
496 |
|
}; |
497 |
|
|
498 |
|
#ifdef JS_EVAL_CACHE_METERING |
499 |
|
static void |
500 |
|
DumpEvalCacheMeter(JSContext *cx) |
501 |
|
{ |
502 |
|
struct { |
503 |
|
const char *name; |
504 |
|
ptrdiff_t offset; |
505 |
|
} table[] = { |
506 |
|
#define frob(x) { #x, offsetof(JSEvalCacheMeter, x) } |
507 |
|
EVAL_CACHE_METER_LIST(frob) |
508 |
|
#undef frob |
509 |
|
}; |
510 |
|
JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter; |
511 |
|
|
512 |
|
static JSAutoFile fp; |
513 |
|
if (!fp) { |
514 |
|
fp.open("/tmp/evalcache.stats", "w"); |
515 |
|
if (!fp) |
516 |
|
return; |
517 |
|
} |
518 |
|
|
519 |
|
fprintf(fp, "eval cache meter (%p):\n", |
520 |
|
#ifdef JS_THREADSAFE |
521 |
|
(void *) cx->thread |
522 |
|
#else |
523 |
|
(void *) cx->runtime |
524 |
|
#endif |
525 |
|
); |
526 |
|
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) { |
527 |
|
fprintf(fp, "%-8.8s %llu\n", |
528 |
|
table[i].name, *(uint64 *)((uint8 *)ecm + table[i].offset)); |
529 |
|
} |
530 |
|
fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe); |
531 |
|
fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe); |
532 |
|
fflush(fp); |
533 |
|
} |
534 |
|
# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx) |
535 |
|
#endif |
536 |
|
|
537 |
|
#ifdef JS_FUNCTION_METERING |
538 |
|
static void |
539 |
|
DumpFunctionMeter(JSContext *cx) |
540 |
|
{ |
541 |
|
struct { |
542 |
|
const char *name; |
543 |
|
ptrdiff_t offset; |
544 |
|
} table[] = { |
545 |
|
#define frob(x) { #x, offsetof(JSFunctionMeter, x) } |
546 |
|
FUNCTION_KIND_METER_LIST(frob) |
547 |
|
#undef frob |
548 |
|
}; |
549 |
|
JSFunctionMeter *fm = &cx->runtime->functionMeter; |
550 |
|
|
551 |
|
static JSAutoFile fp; |
552 |
|
if (!fp) { |
553 |
|
fp.open("/tmp/function.stats", "a"); |
554 |
|
if (!fp) |
555 |
|
return; |
556 |
|
} |
557 |
|
|
558 |
|
fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename); |
559 |
|
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) { |
560 |
|
fprintf(fp, "%-11.11s %d\n", |
561 |
|
table[i].name, *(int32 *)((uint8 *)fm + table[i].offset)); |
562 |
|
} |
563 |
|
fflush(fp); |
564 |
|
} |
565 |
|
# define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx) |
566 |
|
#endif |
567 |
|
|
568 |
|
#endif /* DEBUG && XP_UNIX */ |
569 |
|
|
570 |
|
#ifndef DUMP_EVAL_CACHE_METER |
571 |
|
# define DUMP_EVAL_CACHE_METER(cx) ((void) 0) |
572 |
|
#endif |
573 |
|
|
574 |
|
#ifndef DUMP_FUNCTION_METER |
575 |
|
# define DUMP_FUNCTION_METER(cx) ((void) 0) |
576 |
|
#endif |
577 |
|
|
578 |
void |
void |
579 |
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) |
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) |
580 |
{ |
{ |
581 |
JSRuntime *rt; |
JSRuntime *rt; |
582 |
JSContextCallback cxCallback; |
JSContextCallback cxCallback; |
583 |
JSBool last; |
JSBool last; |
|
JSArgumentFormatMap *map; |
|
|
JSLocalRootStack *lrs; |
|
|
JSLocalRootChunk *lrc; |
|
584 |
|
|
585 |
rt = cx->runtime; |
rt = cx->runtime; |
586 |
|
#ifdef JS_THREADSAFE |
587 |
|
/* |
588 |
|
* For API compatibility we allow to destroy contexts without a thread in |
589 |
|
* optimized builds. We assume that the embedding knows that an OOM error |
590 |
|
* cannot happen in JS_SetContextThread. |
591 |
|
*/ |
592 |
|
JS_ASSERT(cx->thread && CURRENT_THREAD_IS_ME(cx->thread)); |
593 |
|
if (!cx->thread) |
594 |
|
JS_SetContextThread(cx); |
595 |
|
|
596 |
|
JS_ASSERT_IF(rt->gcRunning, cx->outstandingRequests == 0); |
597 |
|
#endif |
598 |
|
|
599 |
if (mode != JSDCM_NEW_FAILED) { |
if (mode != JSDCM_NEW_FAILED) { |
600 |
cxCallback = rt->cxCallback; |
cxCallback = rt->cxCallback; |
611 |
} |
} |
612 |
} |
} |
613 |
|
|
|
/* Remove cx from context list first. */ |
|
614 |
JS_LOCK_GC(rt); |
JS_LOCK_GC(rt); |
615 |
JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); |
JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); |
616 |
JS_REMOVE_LINK(&cx->links); |
#ifdef JS_THREADSAFE |
617 |
|
/* |
618 |
|
* Typically we are called outside a request, so ensure that the GC is not |
619 |
|
* running before removing the context from rt->contextList, see bug 477021. |
620 |
|
*/ |
621 |
|
if (cx->requestDepth == 0) |
622 |
|
js_WaitForGC(rt); |
623 |
|
js_RevokeGCLocalFreeLists(cx); |
624 |
|
#endif |
625 |
|
JS_REMOVE_LINK(&cx->link); |
626 |
last = (rt->contextList.next == &rt->contextList); |
last = (rt->contextList.next == &rt->contextList); |
627 |
if (last) |
if (last) |
628 |
rt->state = JSRTS_LANDING; |
rt->state = JSRTS_LANDING; |
629 |
|
if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC |
630 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
631 |
js_RevokeGCLocalFreeLists(cx); |
|| cx->requestDepth != 0 |
632 |
#endif |
#endif |
633 |
JS_UNLOCK_GC(rt); |
) { |
634 |
|
JS_ASSERT(!rt->gcRunning); |
635 |
|
|
636 |
|
JS_UNLOCK_GC(rt); |
637 |
|
|
638 |
|
if (last) { |
639 |
|
#ifdef JS_THREADSAFE |
640 |
|
/* |
641 |
|
* If cx is not in a request already, begin one now so that we wait |
642 |
|
* for any racing GC started on a not-last context to finish, before |
643 |
|
* we plow ahead and unpin atoms. Note that even though we begin a |
644 |
|
* request here if necessary, we end all requests on cx below before |
645 |
|
* forcing a final GC. This lets any not-last context destruction |
646 |
|
* racing in another thread try to force or maybe run the GC, but by |
647 |
|
* that point, rt->state will not be JSRTS_UP, and that GC attempt |
648 |
|
* will return early. |
649 |
|
*/ |
650 |
|
if (cx->requestDepth == 0) |
651 |
|
JS_BeginRequest(cx); |
652 |
|
#endif |
653 |
|
|
654 |
|
/* Unlock and clear GC things held by runtime pointers. */ |
655 |
|
js_FinishRuntimeNumberState(cx); |
656 |
|
js_FinishRuntimeStringState(cx); |
657 |
|
|
658 |
|
/* Unpin all common atoms before final GC. */ |
659 |
|
js_FinishCommonAtoms(cx); |
660 |
|
|
661 |
|
/* Clear debugging state to remove GC roots. */ |
662 |
|
JS_ClearAllTraps(cx); |
663 |
|
JS_ClearAllWatchPoints(cx); |
664 |
|
} |
665 |
|
|
666 |
|
/* Remove more GC roots in regExpStatics, then collect garbage. */ |
667 |
|
JS_ClearRegExpRoots(cx); |
668 |
|
|
|
if (last) { |
|
669 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
670 |
/* |
/* |
671 |
* If cx is not in a request already, begin one now so that we wait |
* Destroying a context implicitly calls JS_EndRequest(). Also, we must |
672 |
* for any racing GC started on a not-last context to finish, before |
* end our request here in case we are "last" -- in that event, another |
673 |
* we plow ahead and unpin atoms. Note that even though we begin a |
* js_DestroyContext that was not last might be waiting in the GC for our |
674 |
* request here if necessary, we end all requests on cx below before |
* request to end. We'll let it run below, just before we do the truly |
675 |
* forcing a final GC. This lets any not-last context destruction |
* final GC and then free atom state. |
|
* racing in another thread try to force or maybe run the GC, but by |
|
|
* that point, rt->state will not be JSRTS_UP, and that GC attempt |
|
|
* will return early. |
|
676 |
*/ |
*/ |
677 |
if (cx->requestDepth == 0) |
while (cx->requestDepth != 0) |
678 |
JS_BeginRequest(cx); |
JS_EndRequest(cx); |
679 |
#endif |
#endif |
680 |
|
|
681 |
/* Unlock and clear GC things held by runtime pointers. */ |
if (last) { |
682 |
js_FinishRuntimeNumberState(cx); |
/* Clear builtin functions, which are recreated on demand. */ |
683 |
js_FinishRuntimeStringState(cx); |
memset(rt->builtinFunctions, 0, sizeof rt->builtinFunctions); |
684 |
|
|
685 |
|
js_GC(cx, GC_LAST_CONTEXT); |
686 |
|
DUMP_EVAL_CACHE_METER(cx); |
687 |
|
DUMP_FUNCTION_METER(cx); |
688 |
|
|
689 |
/* Unpin all common atoms before final GC. */ |
/* |
690 |
js_FinishCommonAtoms(cx); |
* Free the script filename table if it exists and is empty. Do this |
691 |
|
* after the last GC to avoid finalizers tripping on free memory. |
692 |
|
*/ |
693 |
|
if (rt->scriptFilenameTable && |
694 |
|
rt->scriptFilenameTable->nentries == 0) { |
695 |
|
js_FinishRuntimeScriptState(rt); |
696 |
|
} |
697 |
|
|
698 |
/* Clear debugging state to remove GC roots. */ |
/* Take the runtime down, now that it has no contexts or atoms. */ |
699 |
JS_ClearAllTraps(cx); |
JS_LOCK_GC(rt); |
700 |
JS_ClearAllWatchPoints(cx); |
rt->state = JSRTS_DOWN; |
701 |
|
JS_NOTIFY_ALL_CONDVAR(rt->stateChange); |
702 |
|
} else { |
703 |
|
if (mode == JSDCM_FORCE_GC) |
704 |
|
js_GC(cx, GC_NORMAL); |
705 |
|
else if (mode == JSDCM_MAYBE_GC) |
706 |
|
JS_MaybeGC(cx); |
707 |
|
JS_LOCK_GC(rt); |
708 |
|
js_WaitForGC(rt); |
709 |
|
} |
710 |
} |
} |
|
|
|
|
/* |
|
|
* Remove more GC roots in regExpStatics, then collect garbage. |
|
|
* XXX anti-modularity alert: we rely on the call to js_RemoveRoot within |
|
|
* XXX this function call to wait for any racing GC to complete, in the |
|
|
* XXX case where JS_DestroyContext is called outside of a request on cx |
|
|
*/ |
|
|
js_FreeRegExpStatics(cx, &cx->regExpStatics); |
|
|
|
|
711 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
712 |
/* |
js_ClearContextThread(cx); |
|
* Destroying a context implicitly calls JS_EndRequest(). Also, we must |
|
|
* end our request here in case we are "last" -- in that event, another |
|
|
* js_DestroyContext that was not last might be waiting in the GC for our |
|
|
* request to end. We'll let it run below, just before we do the truly |
|
|
* final GC and then free atom state. |
|
|
* |
|
|
* At this point, cx must be inaccessible to other threads. It's off the |
|
|
* rt->contextList, and it should not be reachable via any object private |
|
|
* data structure. |
|
|
*/ |
|
|
while (cx->requestDepth != 0) |
|
|
JS_EndRequest(cx); |
|
713 |
#endif |
#endif |
714 |
|
JS_UNLOCK_GC(rt); |
715 |
|
FreeContext(cx); |
716 |
|
} |
717 |
|
|
718 |
if (last) { |
static void |
719 |
js_GC(cx, GC_LAST_CONTEXT); |
FreeContext(JSContext *cx) |
720 |
|
{ |
721 |
/* |
JSArgumentFormatMap *map; |
722 |
* Free the script filename table if it exists and is empty. Do this |
JSLocalRootStack *lrs; |
723 |
* after the last GC to avoid finalizers tripping on free memory. |
JSLocalRootChunk *lrc; |
|
*/ |
|
|
if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) |
|
|
js_FinishRuntimeScriptState(rt); |
|
724 |
|
|
725 |
/* Take the runtime down, now that it has no contexts or atoms. */ |
#ifdef JS_THREADSAFE |
726 |
JS_LOCK_GC(rt); |
JS_ASSERT(!cx->thread); |
727 |
rt->state = JSRTS_DOWN; |
#endif |
|
JS_NOTIFY_ALL_CONDVAR(rt->stateChange); |
|
|
JS_UNLOCK_GC(rt); |
|
|
} else { |
|
|
if (mode == JSDCM_FORCE_GC) |
|
|
js_GC(cx, GC_NORMAL); |
|
|
else if (mode == JSDCM_MAYBE_GC) |
|
|
JS_MaybeGC(cx); |
|
|
} |
|
728 |
|
|
729 |
/* Free the stuff hanging off of cx. */ |
/* Free the stuff hanging off of cx. */ |
730 |
|
js_FreeRegExpStatics(cx); |
731 |
|
VOUCH_DOES_NOT_REQUIRE_STACK(); |
732 |
JS_FinishArenaPool(&cx->stackPool); |
JS_FinishArenaPool(&cx->stackPool); |
733 |
JS_FinishArenaPool(&cx->tempPool); |
JS_FinishArenaPool(&cx->tempPool); |
|
JS_FinishArenaPool(&cx->regexpPool); |
|
734 |
|
|
735 |
if (cx->lastMessage) |
if (cx->lastMessage) |
736 |
free(cx->lastMessage); |
free(cx->lastMessage); |
758 |
JS_free(cx, lrs); |
JS_free(cx, lrs); |
759 |
} |
} |
760 |
|
|
|
#ifdef JS_THREADSAFE |
|
|
js_ClearContextThread(cx); |
|
|
#endif |
|
|
|
|
761 |
/* Finally, free cx itself. */ |
/* Finally, free cx itself. */ |
762 |
free(cx); |
free(cx); |
763 |
} |
} |
768 |
JSCList *cl; |
JSCList *cl; |
769 |
|
|
770 |
for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) { |
for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) { |
771 |
if (cl == &cx->links) |
if (cl == &cx->link) |
772 |
return JS_TRUE; |
return JS_TRUE; |
773 |
} |
} |
774 |
JS_RUNTIME_METER(rt, deadContexts); |
JS_RUNTIME_METER(rt, deadContexts); |
782 |
|
|
783 |
if (unlocked) |
if (unlocked) |
784 |
JS_LOCK_GC(rt); |
JS_LOCK_GC(rt); |
785 |
cx = (JSContext *) (cx ? cx->links.next : rt->contextList.next); |
cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next); |
786 |
if (&cx->links == &rt->contextList) |
if (&cx->link == &rt->contextList) |
787 |
cx = NULL; |
cx = NULL; |
788 |
*iterp = cx; |
*iterp = cx; |
789 |
if (unlocked) |
if (unlocked) |
791 |
return cx; |
return cx; |
792 |
} |
} |
793 |
|
|
794 |
|
JS_FRIEND_API(JSContext *) |
795 |
|
js_NextActiveContext(JSRuntime *rt, JSContext *cx) |
796 |
|
{ |
797 |
|
JSContext *iter = cx; |
798 |
|
#ifdef JS_THREADSAFE |
799 |
|
while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { |
800 |
|
if (cx->requestDepth) |
801 |
|
break; |
802 |
|
} |
803 |
|
return cx; |
804 |
|
#else |
805 |
|
return js_ContextIterator(rt, JS_FALSE, &iter); |
806 |
|
#endif |
807 |
|
} |
808 |
|
|
809 |
|
#ifdef JS_THREADSAFE |
810 |
|
|
811 |
|
uint32 |
812 |
|
js_CountThreadRequests(JSContext *cx) |
813 |
|
{ |
814 |
|
JSCList *head, *link; |
815 |
|
uint32 nrequests; |
816 |
|
|
817 |
|
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); |
818 |
|
head = &cx->thread->contextList; |
819 |
|
nrequests = 0; |
820 |
|
for (link = head->next; link != head; link = link->next) { |
821 |
|
JSContext *acx = CX_FROM_THREAD_LINKS(link); |
822 |
|
JS_ASSERT(acx->thread == cx->thread); |
823 |
|
if (acx->requestDepth) |
824 |
|
nrequests++; |
825 |
|
} |
826 |
|
return nrequests; |
827 |
|
} |
828 |
|
|
829 |
|
/* |
830 |
|
* If the GC is running and we're called on another thread, wait for this GC |
831 |
|
* activation to finish. We can safely wait here without fear of deadlock (in |
832 |
|
* the case where we are called within a request on another thread's context) |
833 |
|
* because the GC doesn't set rt->gcRunning until after it has waited for all |
834 |
|
* active requests to end. |
835 |
|
* |
836 |
|
* We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid |
837 |
|
* expensive calls when the GC is not running. |
838 |
|
*/ |
839 |
|
void |
840 |
|
js_WaitForGC(JSRuntime *rt) |
841 |
|
{ |
842 |
|
JS_ASSERT_IF(rt->gcRunning, rt->gcLevel > 0); |
843 |
|
if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { |
844 |
|
do { |
845 |
|
JS_AWAIT_GC_DONE(rt); |
846 |
|
} while (rt->gcRunning); |
847 |
|
} |
848 |
|
} |
849 |
|
|
850 |
|
uint32 |
851 |
|
js_DiscountRequestsForGC(JSContext *cx) |
852 |
|
{ |
853 |
|
uint32 requestDebit; |
854 |
|
|
855 |
|
JS_ASSERT(cx->thread); |
856 |
|
JS_ASSERT(cx->runtime->gcThread != cx->thread); |
857 |
|
|
858 |
|
#ifdef JS_TRACER |
859 |
|
if (JS_ON_TRACE(cx)) { |
860 |
|
JS_UNLOCK_GC(cx->runtime); |
861 |
|
js_LeaveTrace(cx); |
862 |
|
JS_LOCK_GC(cx->runtime); |
863 |
|
} |
864 |
|
#endif |
865 |
|
|
866 |
|
requestDebit = js_CountThreadRequests(cx); |
867 |
|
if (requestDebit != 0) { |
868 |
|
JSRuntime *rt = cx->runtime; |
869 |
|
JS_ASSERT(requestDebit <= rt->requestCount); |
870 |
|
rt->requestCount -= requestDebit; |
871 |
|
if (rt->requestCount == 0) |
872 |
|
JS_NOTIFY_REQUEST_DONE(rt); |
873 |
|
} |
874 |
|
return requestDebit; |
875 |
|
} |
876 |
|
|
877 |
|
void |
878 |
|
js_RecountRequestsAfterGC(JSRuntime *rt, uint32 requestDebit) |
879 |
|
{ |
880 |
|
while (rt->gcLevel > 0) { |
881 |
|
JS_ASSERT(rt->gcThread); |
882 |
|
JS_AWAIT_GC_DONE(rt); |
883 |
|
} |
884 |
|
if (requestDebit != 0) |
885 |
|
rt->requestCount += requestDebit; |
886 |
|
} |
887 |
|
|
888 |
|
#endif |
889 |
|
|
890 |
static JSDHashNumber |
static JSDHashNumber |
891 |
resolving_HashKey(JSDHashTable *table, const void *ptr) |
resolving_HashKey(JSDHashTable *table, const void *ptr) |
892 |
{ |
{ |
1234 |
* propagates out of scope. This is needed for compatability |
* propagates out of scope. This is needed for compatability |
1235 |
* with the old scheme. |
* with the old scheme. |
1236 |
*/ |
*/ |
1237 |
if (!cx->fp || !js_ErrorToException(cx, message, reportp)) { |
if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp)) { |
1238 |
js_ReportErrorAgain(cx, message, reportp); |
js_ReportErrorAgain(cx, message, reportp); |
1239 |
} else if (cx->debugHooks->debugErrorHook && cx->errorReporter) { |
} else if (cx->debugHooks->debugErrorHook && cx->errorReporter) { |
1240 |
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook; |
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook; |
1244 |
} |
} |
1245 |
} |
} |
1246 |
|
|
1247 |
|
/* The report must be initially zeroed. */ |
1248 |
|
static void |
1249 |
|
PopulateReportBlame(JSContext *cx, JSErrorReport *report) |
1250 |
|
{ |
1251 |
|
JSStackFrame *fp; |
1252 |
|
|
1253 |
|
/* |
1254 |
|
* Walk stack until we find a frame that is associated with some script |
1255 |
|
* rather than a native frame. |
1256 |
|
*/ |
1257 |
|
for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) { |
1258 |
|
if (fp->regs) { |
1259 |
|
report->filename = fp->script->filename; |
1260 |
|
report->lineno = js_FramePCToLineNumber(cx, fp); |
1261 |
|
break; |
1262 |
|
} |
1263 |
|
} |
1264 |
|
} |
1265 |
|
|
1266 |
/* |
/* |
1267 |
* We don't post an exception in this case, since doing so runs into |
* We don't post an exception in this case, since doing so runs into |
1268 |
* complications of pre-allocating an exception object which required |
* complications of pre-allocating an exception object which required |
1273 |
void |
void |
1274 |
js_ReportOutOfMemory(JSContext *cx) |
js_ReportOutOfMemory(JSContext *cx) |
1275 |
{ |
{ |
1276 |
JSStackFrame *fp; |
#ifdef JS_TRACER |
1277 |
|
/* |
1278 |
|
* If we are in a builtin called directly from trace, don't report an |
1279 |
|
* error. We will retry in the interpreter instead. |
1280 |
|
*/ |
1281 |
|
if (JS_ON_TRACE(cx) && !cx->bailExit) |
1282 |
|
return; |
1283 |
|
#endif |
1284 |
|
|
1285 |
JSErrorReport report; |
JSErrorReport report; |
1286 |
JSErrorReporter onError = cx->errorReporter; |
JSErrorReporter onError = cx->errorReporter; |
1287 |
|
|
1294 |
memset(&report, 0, sizeof (struct JSErrorReport)); |
memset(&report, 0, sizeof (struct JSErrorReport)); |
1295 |
report.flags = JSREPORT_ERROR; |
report.flags = JSREPORT_ERROR; |
1296 |
report.errorNumber = JSMSG_OUT_OF_MEMORY; |
report.errorNumber = JSMSG_OUT_OF_MEMORY; |
1297 |
|
PopulateReportBlame(cx, &report); |
|
/* |
|
|
* Walk stack until we find a frame that is associated with some script |
|
|
* rather than a native frame. |
|
|
*/ |
|
|
for (fp = cx->fp; fp; fp = fp->down) { |
|
|
if (fp->regs) { |
|
|
report.filename = fp->script->filename; |
|
|
report.lineno = js_FramePCToLineNumber(cx, fp); |
|
|
break; |
|
|
} |
|
|
} |
|
1298 |
|
|
1299 |
/* |
/* |
1300 |
* If debugErrorHook is present then we give it a chance to veto sending |
* If debugErrorHook is present then we give it a chance to veto sending |
1340 |
char *message; |
char *message; |
1341 |
jschar *ucmessage; |
jschar *ucmessage; |
1342 |
size_t messagelen; |
size_t messagelen; |
|
JSStackFrame *fp; |
|
1343 |
JSErrorReport report; |
JSErrorReport report; |
1344 |
JSBool warning; |
JSBool warning; |
1345 |
|
|
1355 |
report.flags = flags; |
report.flags = flags; |
1356 |
report.errorNumber = JSMSG_USER_DEFINED_ERROR; |
report.errorNumber = JSMSG_USER_DEFINED_ERROR; |
1357 |
report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); |
report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); |
1358 |
|
PopulateReportBlame(cx, &report); |
|
/* Find the top-most active script frame, for best line number blame. */ |
|
|
for (fp = cx->fp; fp; fp = fp->down) { |
|
|
if (fp->regs) { |
|
|
report.filename = fp->script->filename; |
|
|
report.lineno = js_FramePCToLineNumber(cx, fp); |
|
|
break; |
|
|
} |
|
|
} |
|
1359 |
|
|
1360 |
warning = JSREPORT_IS_WARNING(report.flags); |
warning = JSREPORT_IS_WARNING(report.flags); |
1361 |
if (warning && JS_HAS_WERROR_OPTION(cx)) { |
if (warning && JS_HAS_WERROR_OPTION(cx)) { |
1545 |
void *userRef, const uintN errorNumber, |
void *userRef, const uintN errorNumber, |
1546 |
JSBool charArgs, va_list ap) |
JSBool charArgs, va_list ap) |
1547 |
{ |
{ |
|
JSStackFrame *fp; |
|
1548 |
JSErrorReport report; |
JSErrorReport report; |
1549 |
char *message; |
char *message; |
1550 |
JSBool warning; |
JSBool warning; |
1555 |
memset(&report, 0, sizeof (struct JSErrorReport)); |
memset(&report, 0, sizeof (struct JSErrorReport)); |
1556 |
report.flags = flags; |
report.flags = flags; |
1557 |
report.errorNumber = errorNumber; |
report.errorNumber = errorNumber; |
1558 |
|
PopulateReportBlame(cx, &report); |
|
/* |
|
|
* If we can't find out where the error was based on the current frame, |
|
|
* see if the next frame has a script/pc combo we can use. |
|
|
*/ |
|
|
for (fp = cx->fp; fp; fp = fp->down) { |
|
|
if (fp->regs) { |
|
|
report.filename = fp->script->filename; |
|
|
report.lineno = js_FramePCToLineNumber(cx, fp); |
|
|
break; |
|
|
} |
|
|
} |
|
1559 |
|
|
1560 |
if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, |
if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, |
1561 |
&message, &report, &warning, charArgs, ap)) { |
&message, &report, &warning, charArgs, ap)) { |
1699 |
|
|
1700 |
#if defined DEBUG && defined XP_UNIX |
#if defined DEBUG && defined XP_UNIX |
1701 |
/* For gdb usage. */ |
/* For gdb usage. */ |
1702 |
void js_traceon(JSContext *cx) { cx->tracefp = stderr; } |
void js_traceon(JSContext *cx) { cx->tracefp = stderr; cx->tracePrevPc = NULL; } |
1703 |
void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } |
void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } |
1704 |
#endif |
#endif |
1705 |
|
|
1719 |
} |
} |
1720 |
|
|
1721 |
JSBool |
JSBool |
1722 |
js_ResetOperationCount(JSContext *cx) |
js_InvokeOperationCallback(JSContext *cx) |
1723 |
{ |
{ |
1724 |
JSScript *script; |
JS_ASSERT(cx->operationCallbackFlag); |
1725 |
|
|
1726 |
|
/* |
1727 |
|
* Reset the callback flag first, then yield. If another thread is racing |
1728 |
|
* us here we will accumulate another callback request which will be |
1729 |
|
* serviced at the next opportunity. |
1730 |
|
*/ |
1731 |
|
cx->operationCallbackFlag = 0; |
1732 |
|
|
1733 |
|
/* |
1734 |
|
* Unless we are going to run the GC, we automatically yield the current |
1735 |
|
* context every time the operation callback is hit since we might be |
1736 |
|
* called as a result of an impending GC, which would deadlock if we do |
1737 |
|
* not yield. Operation callbacks are supposed to happen rarely (seconds, |
1738 |
|
* not milliseconds) so it is acceptable to yield at every callback. |
1739 |
|
*/ |
1740 |
|
if (cx->runtime->gcIsNeeded) |
1741 |
|
js_GC(cx, GC_NORMAL); |
1742 |
|
#ifdef JS_THREADSAFE |
1743 |
|
else |
1744 |
|
JS_YieldRequest(cx); |
1745 |
|
#endif |
1746 |
|
|
1747 |
|
JSOperationCallback cb = cx->operationCallback; |
1748 |
|
|
1749 |
JS_ASSERT(cx->operationCount <= 0); |
/* |
1750 |
JS_ASSERT(cx->operationLimit > 0); |
* Important: Additional callbacks can occur inside the callback handler |
1751 |
|
* if it re-enters the JS engine. The embedding must ensure that the |
1752 |
|
* callback is disconnected before attempting such re-entry. |
1753 |
|
*/ |
1754 |
|
|
1755 |
cx->operationCount = (int32) cx->operationLimit; |
return !cb || cb(cx); |
1756 |
if (cx->operationCallbackIsSet) |
} |
|
return cx->operationCallback(cx); |
|
1757 |
|
|
1758 |
if (cx->operationCallback) { |
void |
1759 |
/* |
js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked) |
1760 |
* Invoke the deprecated branch callback. It may be called only when |
{ |
1761 |
* the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK |
JSContext *acx, *iter; |
1762 |
* is set. |
#ifdef JS_THREADSAFE |
1763 |
*/ |
if (!gcLocked) |
1764 |
script = cx->fp ? cx->fp->script : NULL; |
JS_LOCK_GC(rt); |
1765 |
if (script || JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK)) |
#endif |
1766 |
return ((JSBranchCallback) cx->operationCallback)(cx, script); |
iter = NULL; |
1767 |
|
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter))) |
1768 |
|
JS_TriggerOperationCallback(acx); |
1769 |
|
#ifdef JS_THREADSAFE |
1770 |
|
if (!gcLocked) |
1771 |
|
JS_UNLOCK_GC(rt); |
1772 |
|
#endif |
1773 |
|
} |
1774 |
|
|
1775 |
|
JSStackFrame * |
1776 |
|
js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) |
1777 |
|
{ |
1778 |
|
if (!fp) |
1779 |
|
fp = js_GetTopStackFrame(cx); |
1780 |
|
while (fp) { |
1781 |
|
if (fp->script) |
1782 |
|
return fp; |
1783 |
|
fp = fp->down; |
1784 |
} |
} |
1785 |
return JS_TRUE; |
return NULL; |
1786 |
|
} |
1787 |
|
|
1788 |
|
jsbytecode* |
1789 |
|
js_GetCurrentBytecodePC(JSContext* cx) |
1790 |
|
{ |
1791 |
|
jsbytecode *pc, *imacpc; |
1792 |
|
|
1793 |
|
#ifdef JS_TRACER |
1794 |
|
if (JS_ON_TRACE(cx)) { |
1795 |
|
pc = cx->bailExit->pc; |
1796 |
|
imacpc = cx->bailExit->imacpc; |
1797 |
|
} else |
1798 |
|
#endif |
1799 |
|
{ |
1800 |
|
JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */ |
1801 |
|
JSStackFrame* fp = cx->fp; |
1802 |
|
if (fp && fp->regs) { |
1803 |
|
pc = fp->regs->pc; |
1804 |
|
imacpc = fp->imacpc; |
1805 |
|
} else { |
1806 |
|
return NULL; |
1807 |
|
} |
1808 |
|
} |
1809 |
|
|
1810 |
|
/* |
1811 |
|
* If we are inside GetProperty_tn or similar, return a pointer to the |
1812 |
|
* current instruction in the script, not the CALL instruction in the |
1813 |
|
* imacro, for the benefit of callers doing bytecode inspection. |
1814 |
|
*/ |
1815 |
|
return (*pc == JSOP_CALL && imacpc) ? imacpc : pc; |
1816 |
} |
} |