1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
40 /*
41 * JS execution context.
42 */
43 #include "jsstddef.h"
44 #include <stdarg.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsarena.h" /* Added by JSIFY */
49 #include "jsutil.h" /* Added by JSIFY */
50 #include "jsclist.h"
51 #include "jsprf.h"
52 #include "jsatom.h"
53 #include "jscntxt.h"
54 #include "jsconfig.h"
55 #include "jsdbgapi.h"
56 #include "jsexn.h"
57 #include "jsgc.h"
58 #include "jslock.h"
59 #include "jsnum.h"
60 #include "jsobj.h"
61 #include "jsopcode.h"
62 #include "jsscan.h"
63 #include "jsscript.h"
64 #include "jsstr.h"
66 JSContext *
67 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
68 {
69 JSContext *cx;
70 JSBool ok, first;
72 cx = (JSContext *) malloc(sizeof *cx);
73 if (!cx)
74 return NULL;
75 memset(cx, 0, sizeof *cx);
77 cx->runtime = rt;
78 #if JS_STACK_GROWTH_DIRECTION > 0
79 cx->stackLimit = (jsuword)-1;
80 #endif
81 #ifdef JS_THREADSAFE
82 js_InitContextForLocking(cx);
83 #endif
85 JS_LOCK_GC(rt);
86 for (;;) {
87 first = (rt->contextList.next == &rt->contextList);
88 if (rt->state == JSRTS_UP) {
89 JS_ASSERT(!first);
90 break;
91 }
92 if (rt->state == JSRTS_DOWN) {
93 JS_ASSERT(first);
94 rt->state = JSRTS_LAUNCHING;
95 break;
96 }
97 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
98 }
99 JS_APPEND_LINK(&cx->links, &rt->contextList);
100 JS_UNLOCK_GC(rt);
102 /*
103 * First we do the infallible, every-time per-context initializations.
104 * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
105 * or the stuff under 'if (first)' below) fail, at least the version
106 * and arena-pools will be valid and safe to use (say, from the last GC
107 * done by js_DestroyContext).
108 */
109 cx->version = JSVERSION_DEFAULT;
110 cx->jsop_eq = JSOP_EQ;
111 cx->jsop_ne = JSOP_NE;
112 JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));
113 JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));
115 #if JS_HAS_REGEXPS
116 if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
117 js_DestroyContext(cx, JS_NO_GC);
118 return NULL;
119 }
120 #endif
121 #if JS_HAS_EXCEPTIONS
122 cx->throwing = JS_FALSE;
123 #endif
125 /*
126 * If cx is the first context on this runtime, initialize well-known atoms,
127 * keywords, numbers, and strings. If one of these steps should fail, the
128 * runtime will be left in a partially initialized state, with zeroes and
129 * nulls stored in the default-initialized remainder of the struct. We'll
130 * clean the runtime up under js_DestroyContext, because cx will be "last"
131 * as well as "first".
132 */
133 if (first) {
134 ok = (rt->atomState.liveAtoms == 0)
135 ? js_InitAtomState(cx, &rt->atomState)
136 : js_InitPinnedAtoms(cx, &rt->atomState);
137 if (ok)
138 ok = js_InitScanner(cx);
139 if (ok)
140 ok = js_InitRuntimeNumberState(cx);
141 if (ok)
142 ok = js_InitRuntimeStringState(cx);
143 if (!ok) {
144 js_DestroyContext(cx, JS_NO_GC);
145 return NULL;
146 }
148 JS_LOCK_GC(rt);
149 rt->state = JSRTS_UP;
150 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
151 JS_UNLOCK_GC(rt);
152 }
154 return cx;
155 }
157 void
158 js_DestroyContext(JSContext *cx, JSGCMode gcmode)
159 {
160 JSRuntime *rt;
161 JSBool last;
162 JSArgumentFormatMap *map;
164 rt = cx->runtime;
166 /* Remove cx from context list first. */
167 JS_LOCK_GC(rt);
168 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
169 JS_REMOVE_LINK(&cx->links);
170 last = (rt->contextList.next == &rt->contextList);
171 if (last)
172 rt->state = JSRTS_LANDING;
173 JS_UNLOCK_GC(rt);
175 if (last) {
176 #ifdef JS_THREADSAFE
177 /*
178 * If cx is not in a request already, begin one now so that we wait
179 * for any racing GC started on a not-last context to finish, before
180 * we plow ahead and unpin atoms. Note that even though we begin a
181 * request here if necessary, we end all requests on cx below before
182 * forcing a final GC. This lets any not-last context destruction
183 * racing in another thread try to force or maybe run the GC, but by
184 * that point, rt->state will not be JSRTS_UP, and that GC attempt
185 * will return early.
186 */
187 if (cx->requestDepth == 0)
188 JS_BeginRequest(cx);
189 #endif
191 /* Unpin all pinned atoms before final GC. */
192 js_UnpinPinnedAtoms(&rt->atomState);
194 /* Unlock and clear GC things held by runtime pointers. */
195 js_FinishRuntimeNumberState(cx);
196 js_FinishRuntimeStringState(cx);
198 /* Clear debugging state to remove GC roots. */
199 JS_ClearAllTraps(cx);
200 JS_ClearAllWatchPoints(cx);
201 }
203 #if JS_HAS_REGEXPS
204 /*
205 * Remove more GC roots in regExpStatics, then collect garbage.
206 * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
207 * XXX this function call to wait for any racing GC to complete, in the
208 * XXX case where JS_DestroyContext is called outside of a request on cx
209 */
210 js_FreeRegExpStatics(cx, &cx->regExpStatics);
211 #endif
213 #ifdef JS_THREADSAFE
214 /*
215 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
216 * end our request here in case we are "last" -- in that event, another
217 * js_DestroyContext that was not last might be waiting in the GC for our
218 * request to end. We'll let it run below, just before we do the truly
219 * final GC and then free atom state.
220 *
221 * At this point, cx must be inaccessible to other threads. It's off the
222 * rt->contextList, and it should not be reachable via any object private
223 * data structure.
224 */
225 while (cx->requestDepth != 0)
226 JS_EndRequest(cx);
227 #endif
229 if (last) {
230 /* Always force, so we wait for any racing GC to finish. */
231 js_ForceGC(cx, GC_LAST_CONTEXT);
233 /* Iterate until no finalizer removes a GC root or lock. */
234 while (rt->gcPoke)
235 js_GC(cx, GC_LAST_CONTEXT);
237 /* Try to free atom state, now that no unrooted scripts survive. */
238 if (rt->atomState.liveAtoms == 0)
239 js_FreeAtomState(cx, &rt->atomState);
241 /* Take the runtime down, now that it has no contexts or atoms. */
242 JS_LOCK_GC(rt);
243 rt->state = JSRTS_DOWN;
244 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
245 JS_UNLOCK_GC(rt);
246 } else {
247 if (gcmode == JS_FORCE_GC)
248 js_ForceGC(cx, 0);
249 else if (gcmode == JS_MAYBE_GC)
250 JS_MaybeGC(cx);
251 }
253 /* Free the stuff hanging off of cx. */
254 JS_FinishArenaPool(&cx->stackPool);
255 JS_FinishArenaPool(&cx->tempPool);
256 if (cx->lastMessage)
257 free(cx->lastMessage);
259 /* Remove any argument formatters. */
260 map = cx->argumentFormatMap;
261 while (map) {
262 JSArgumentFormatMap *temp = map;
263 map = map->next;
264 JS_free(cx, temp);
265 }
267 /* Destroy the resolve recursion damper. */
268 if (cx->resolvingTable) {
269 JS_DHashTableDestroy(cx->resolvingTable);
270 cx->resolvingTable = NULL;
271 }
273 /* Finally, free cx itself. */
274 free(cx);
275 }
277 JSBool
278 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
279 {
280 JSCList *cl;
282 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
283 if (cl == &cx->links)
284 return JS_TRUE;
285 }
286 JS_RUNTIME_METER(rt, deadContexts);
287 return JS_FALSE;
288 }
290 JSContext *
291 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
292 {
293 JSContext *cx = *iterp;
295 if (unlocked)
296 JS_LOCK_GC(rt);
297 if (!cx)
298 cx = (JSContext *)&rt->contextList;
299 cx = (JSContext *)cx->links.next;
300 if (&cx->links == &rt->contextList)
301 cx = NULL;
302 *iterp = cx;
303 if (unlocked)
304 JS_UNLOCK_GC(rt);
305 return cx;
306 }
308 static void
309 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
310 {
311 /*
312 * Check the error report, and set a JavaScript-catchable exception
313 * if the error is defined to have an associated exception. If an
314 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
315 * on the error report, and exception-aware hosts should ignore it.
316 */
317 if (reportp && reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
318 reportp->flags |= JSREPORT_EXCEPTION;
320 #if JS_HAS_ERROR_EXCEPTIONS
321 /*
322 * Call the error reporter only if an exception wasn't raised.
323 *
324 * If an exception was raised, then we call the debugErrorHook
325 * (if present) to give it a chance to see the error before it
326 * propagates out of scope. This is needed for compatability
327 * with the old scheme.
328 */
329 if (!js_ErrorToException(cx, message, reportp)) {
330 js_ReportErrorAgain(cx, message, reportp);
331 } else if (cx->runtime->debugErrorHook && cx->errorReporter) {
332 JSDebugErrorHook hook = cx->runtime->debugErrorHook;
333 /* test local in case debugErrorHook changed on another thread */
334 if (hook)
335 hook(cx, message, reportp, cx->runtime->debugErrorHookData);
336 }
337 #else
338 js_ReportErrorAgain(cx, message, reportp);
339 #endif
340 }
342 /*
343 * We don't post an exception in this case, since doing so runs into
344 * complications of pre-allocating an exception object which required
345 * running the Exception class initializer early etc.
346 * Instead we just invoke the errorReporter with an "Out Of Memory"
347 * type message, and then hope the process ends swiftly.
348 */
349 void
350 js_ReportOutOfMemory(JSContext *cx, JSErrorCallback callback)
351 {
352 JSStackFrame *fp;
353 JSErrorReport report;
354 JSErrorReporter onError = cx->errorReporter;
356 /* Get the message for this error, but we won't expand any arguments. */
357 const JSErrorFormatString *efs = callback(NULL, NULL, JSMSG_OUT_OF_MEMORY);
358 const char *msg = efs ? efs->format : "Out of memory";
360 /* Fill out the report, but don't do anything that requires allocation. */
361 memset(&report, 0, sizeof (struct JSErrorReport));
362 report.flags = JSREPORT_ERROR;
363 report.errorNumber = JSMSG_OUT_OF_MEMORY;
365 /*
366 * Walk stack until we find a frame that is associated with some script
367 * rather than a native frame.
368 */
369 for (fp = cx->fp; fp; fp = fp->down) {
370 if (fp->script && fp->pc) {
371 report.filename = fp->script->filename;
372 report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
373 break;
374 }
375 }
377 /*
378 * If debugErrorHook is present then we give it a chance to veto
379 * sending the error on to the regular ErrorReporter.
380 */
381 if (onError) {
382 JSDebugErrorHook hook = cx->runtime->debugErrorHook;
383 if (hook &&
384 !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {
385 onError = NULL;
386 }
387 }
389 if (onError)
390 onError(cx, msg, &report);
391 }
393 JSBool
394 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
395 {
396 char *last;
397 JSStackFrame *fp;
398 JSErrorReport report;
399 JSBool warning;
401 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
402 return JS_TRUE;
404 last = JS_vsmprintf(format, ap);
405 if (!last)
406 return JS_FALSE;
408 memset(&report, 0, sizeof (struct JSErrorReport));
409 report.flags = flags;
411 /* Find the top-most active script frame, for best line number blame. */
412 for (fp = cx->fp; fp; fp = fp->down) {
413 if (fp->script && fp->pc) {
414 report.filename = fp->script->filename;
415 report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
416 break;
417 }
418 }
420 warning = JSREPORT_IS_WARNING(report.flags);
421 if (warning && JS_HAS_WERROR_OPTION(cx)) {
422 report.flags &= ~JSREPORT_WARNING;
423 warning = JS_FALSE;
424 }
426 ReportError(cx, last, &report);
427 free(last);
428 return warning;
429 }
431 /*
432 * The arguments from ap need to be packaged up into an array and stored
433 * into the report struct.
434 *
435 * The format string addressed by the error number may contain operands
436 * identified by the format {N}, where N is a decimal digit. Each of these
437 * is to be replaced by the Nth argument from the va_list. The complete
438 * message is placed into reportp->ucmessage converted to a JSString.
439 *
440 * Returns true if the expansion succeeds (can fail if out of memory).
441 */
442 JSBool
443 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
444 void *userRef, const uintN errorNumber,
445 char **messagep, JSErrorReport *reportp,
446 JSBool *warningp, JSBool charArgs, va_list ap)
447 {
448 const JSErrorFormatString *efs;
449 int i;
450 int argCount;
452 *warningp = JSREPORT_IS_WARNING(reportp->flags);
453 if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
454 reportp->flags &= ~JSREPORT_WARNING;
455 *warningp = JS_FALSE;
456 }
458 *messagep = NULL;
459 if (callback) {
460 efs = callback(userRef, NULL, errorNumber);
461 if (efs) {
462 size_t totalArgsLength = 0;
463 size_t argLengths[10]; /* only {0} thru {9} supported */
464 argCount = efs->argCount;
465 JS_ASSERT(argCount <= 10);
466 if (argCount > 0) {
467 /*
468 * Gather the arguments into an array, and accumulate
469 * their sizes. We allocate 1 more than necessary and
470 * null it out to act as the caboose when we free the
471 * pointers later.
472 */
473 reportp->messageArgs = (const jschar **)
474 JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
475 if (!reportp->messageArgs)
476 return JS_FALSE;
477 reportp->messageArgs[argCount] = NULL;
478 for (i = 0; i < argCount; i++) {
479 if (charArgs) {
480 char *charArg = va_arg(ap, char *);
481 reportp->messageArgs[i]
482 = js_InflateString(cx, charArg, strlen(charArg));
483 if (!reportp->messageArgs[i])
484 goto error;
485 }
486 else
487 reportp->messageArgs[i] = va_arg(ap, jschar *);
488 argLengths[i] = js_strlen(reportp->messageArgs[i]);
489 totalArgsLength += argLengths[i];
490 }
491 /* NULL-terminate for easy copying. */
492 reportp->messageArgs[i] = NULL;
493 }
494 /*
495 * Parse the error format, substituting the argument X
496 * for {X} in the format.
497 */
498 if (argCount > 0) {
499 if (efs->format) {
500 const char *fmt;
501 const jschar *arg;
502 jschar *out;
503 int expandedArgs = 0;
504 size_t expandedLength
505 = strlen(efs->format)
506 - (3 * argCount) /* exclude the {n} */
507 + totalArgsLength;
508 /*
509 * Note - the above calculation assumes that each argument
510 * is used once and only once in the expansion !!!
511 */
512 reportp->ucmessage = out = (jschar *)
513 JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
514 if (!out)
515 goto error;
516 fmt = efs->format;
517 while (*fmt) {
518 if (*fmt == '{') {
519 if (isdigit(fmt[1])) {
520 int d = JS7_UNDEC(fmt[1]);
521 JS_ASSERT(expandedArgs < argCount);
522 arg = reportp->messageArgs[d];
523 js_strncpy(out, arg, argLengths[d]);
524 out += argLengths[d];
525 fmt += 3;
526 expandedArgs++;
527 continue;
528 }
529 }
530 /*
531 * is this kosher?
532 */
533 *out++ = (unsigned char)(*fmt++);
534 }
535 JS_ASSERT(expandedArgs == argCount);
536 *out = 0;
537 *messagep =
538 js_DeflateString(cx, reportp->ucmessage,
539 (size_t)(out - reportp->ucmessage));
540 if (!*messagep)
541 goto error;
542 }
543 } else {
544 /*
545 * Zero arguments: the format string (if it exists) is the
546 * entire message.
547 */
548 if (efs->format) {
549 *messagep = JS_strdup(cx, efs->format);
550 if (!*messagep)
551 goto error;
552 reportp->ucmessage
553 = js_InflateString(cx, *messagep, strlen(*messagep));
554 if (!reportp->ucmessage)
555 goto error;
556 }
557 }
558 }
559 }
560 if (*messagep == NULL) {
561 /* where's the right place for this ??? */
562 const char *defaultErrorMessage
563 = "No error message available for error number %d";
564 size_t nbytes = strlen(defaultErrorMessage) + 16;
565 *messagep = (char *)JS_malloc(cx, nbytes);
566 if (!*messagep)
567 goto error;
568 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
569 }
570 return JS_TRUE;
572 error:
573 if (reportp->messageArgs) {
574 i = 0;
575 while (reportp->messageArgs[i])
576 JS_free(cx, (void *)reportp->messageArgs[i++]);
577 JS_free(cx, (void *)reportp->messageArgs);
578 reportp->messageArgs = NULL;
579 }
580 if (reportp->ucmessage) {
581 JS_free(cx, (void *)reportp->ucmessage);
582 reportp->ucmessage = NULL;
583 }
584 if (*messagep) {
585 JS_free(cx, (void *)*messagep);
586 *messagep = NULL;
587 }
588 return JS_FALSE;
589 }
591 JSBool
592 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
593 void *userRef, const uintN errorNumber,
594 JSBool charArgs, va_list ap)
595 {
596 JSStackFrame *fp;
597 JSErrorReport report;
598 char *message;
599 JSBool warning;
601 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
602 return JS_TRUE;
604 memset(&report, 0, sizeof (struct JSErrorReport));
605 report.flags = flags;
606 report.errorNumber = errorNumber;
608 /*
609 * If we can't find out where the error was based on the current frame,
610 * see if the next frame has a script/pc combo we can use.
611 */
612 for (fp = cx->fp; fp; fp = fp->down) {
613 if (fp->script && fp->pc) {
614 report.filename = fp->script->filename;
615 report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
616 break;
617 }
618 }
620 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
621 &message, &report, &warning, charArgs, ap)) {
622 return JS_FALSE;
623 }
625 ReportError(cx, message, &report);
627 if (message)
628 JS_free(cx, message);
629 if (report.messageArgs) {
630 int i = 0;
631 while (report.messageArgs[i])
632 JS_free(cx, (void *)report.messageArgs[i++]);
633 JS_free(cx, (void *)report.messageArgs);
634 }
635 if (report.ucmessage)
636 JS_free(cx, (void *)report.ucmessage);
638 return warning;
639 }
641 JS_FRIEND_API(void)
642 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
643 {
644 JSErrorReporter onError;
646 if (!message)
647 return;
649 if (cx->lastMessage)
650 free(cx->lastMessage);
651 cx->lastMessage = JS_strdup(cx, message);
652 if (!cx->lastMessage)
653 return;
654 onError = cx->errorReporter;
656 /*
657 * If debugErrorHook is present then we give it a chance to veto
658 * sending the error on to the regular ErrorReporter.
659 */
660 if (onError) {
661 JSDebugErrorHook hook = cx->runtime->debugErrorHook;
662 if (hook &&
663 !hook(cx, cx->lastMessage, reportp,
664 cx->runtime->debugErrorHookData)) {
665 onError = NULL;
666 }
667 }
668 if (onError)
669 onError(cx, cx->lastMessage, reportp);
670 }
672 void
673 js_ReportIsNotDefined(JSContext *cx, const char *name)
674 {
675 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
676 }
678 #if defined DEBUG && defined XP_UNIX
679 /* For gdb usage. */
680 void js_traceon(JSContext *cx) { cx->tracefp = stderr; }
681 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
682 #endif
684 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
685 #if JS_HAS_DFLT_MSG_STRINGS
686 #define MSG_DEF(name, number, count, exception, format) \
687 { format, count } ,
688 #else
689 #define MSG_DEF(name, number, count, exception, format) \
690 { NULL, count } ,
691 #endif
692 #include "js.msg"
693 #undef MSG_DEF
694 };
696 const JSErrorFormatString *
697 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
698 {
699 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
700 return &js_ErrorFormatString[errorNumber];
701 return NULL;
702 }