js/src/jscntxt.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:cdf1ba6b8ab7
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 * JS execution context.
9 */
10
11 #include "jscntxtinlines.h"
12
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/MemoryReporting.h"
16
17 #include <ctype.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #ifdef ANDROID
21 # include <android/log.h>
22 # include <fstream>
23 # include <string>
24 #endif // ANDROID
25
26 #include "jsatom.h"
27 #include "jscompartment.h"
28 #include "jsexn.h"
29 #include "jsfun.h"
30 #include "jsgc.h"
31 #include "jsiter.h"
32 #include "jsobj.h"
33 #include "jsopcode.h"
34 #include "jsprf.h"
35 #include "jspubtd.h"
36 #include "jsscript.h"
37 #include "jsstr.h"
38 #include "jstypes.h"
39 #include "jswatchpoint.h"
40 #include "jsworkers.h"
41
42 #include "gc/Marking.h"
43 #ifdef JS_ION
44 #include "jit/Ion.h"
45 #endif
46 #include "js/CharacterEncoding.h"
47 #include "js/OldDebugAPI.h"
48 #include "vm/Shape.h"
49 #include "yarr/BumpPointerAllocator.h"
50
51 #include "jsobjinlines.h"
52 #include "jsscriptinlines.h"
53
54 #include "vm/Stack-inl.h"
55
56 using namespace js;
57 using namespace js::gc;
58
59 using mozilla::DebugOnly;
60 using mozilla::PodArrayZero;
61 using mozilla::PodZero;
62 using mozilla::PointerRangeSize;
63
64 bool
65 js::AutoCycleDetector::init()
66 {
67 ObjectSet &set = cx->cycleDetectorSet;
68 hashsetAddPointer = set.lookupForAdd(obj);
69 if (!hashsetAddPointer) {
70 if (!set.add(hashsetAddPointer, obj))
71 return false;
72 cyclic = false;
73 hashsetGenerationAtInit = set.generation();
74 }
75 return true;
76 }
77
78 js::AutoCycleDetector::~AutoCycleDetector()
79 {
80 if (!cyclic) {
81 if (hashsetGenerationAtInit == cx->cycleDetectorSet.generation())
82 cx->cycleDetectorSet.remove(hashsetAddPointer);
83 else
84 cx->cycleDetectorSet.remove(obj);
85 }
86 }
87
88 void
89 js::TraceCycleDetectionSet(JSTracer *trc, js::ObjectSet &set)
90 {
91 for (js::ObjectSet::Enum e(set); !e.empty(); e.popFront()) {
92 JSObject *prior = e.front();
93 MarkObjectRoot(trc, const_cast<JSObject **>(&e.front()), "cycle detector table entry");
94 if (prior != e.front())
95 e.rekeyFront(e.front());
96 }
97 }
98
99 void
100 JSCompartment::sweepCallsiteClones()
101 {
102 if (callsiteClones.initialized()) {
103 for (CallsiteCloneTable::Enum e(callsiteClones); !e.empty(); e.popFront()) {
104 CallsiteCloneKey key = e.front().key();
105 JSFunction *fun = e.front().value();
106 if (!IsScriptMarked(&key.script) || !IsObjectMarked(&fun))
107 e.removeFront();
108 }
109 }
110 }
111
112 JSFunction *
113 js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun,
114 JSScript *script, jsbytecode *pc)
115 {
116 JS_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite());
117 JS_ASSERT(!fun->nonLazyScript()->enclosingStaticScope());
118 JS_ASSERT(types::UseNewTypeForClone(fun));
119
120 /*
121 * If we start allocating function objects in the nursery, then the callsite
122 * clone table will need a postbarrier.
123 */
124 JS_ASSERT(fun->isTenured());
125
126 if (!table.initialized())
127 return nullptr;
128
129 CallsiteCloneTable::Ptr p = table.readonlyThreadsafeLookup(CallsiteCloneKey(fun, script, script->pcToOffset(pc)));
130 if (p)
131 return p->value();
132
133 return nullptr;
134 }
135
136 JSFunction *
137 js::CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript script, jsbytecode *pc)
138 {
139 if (JSFunction *clone = ExistingCloneFunctionAtCallsite(cx->compartment()->callsiteClones, fun, script, pc))
140 return clone;
141
142 RootedObject parent(cx, fun->environment());
143 JSFunction *clone = CloneFunctionObject(cx, fun, parent);
144 if (!clone)
145 return nullptr;
146
147 /*
148 * Store a link back to the original for function.caller and avoid cloning
149 * clones.
150 */
151 clone->nonLazyScript()->setIsCallsiteClone(fun);
152
153 typedef CallsiteCloneKey Key;
154 typedef CallsiteCloneTable Table;
155
156 Table &table = cx->compartment()->callsiteClones;
157 if (!table.initialized() && !table.init())
158 return nullptr;
159
160 if (!table.putNew(Key(fun, script, script->pcToOffset(pc)), clone))
161 return nullptr;
162
163 return clone;
164 }
165
166 JSContext *
167 js::NewContext(JSRuntime *rt, size_t stackChunkSize)
168 {
169 JS_AbortIfWrongThread(rt);
170
171 JSContext *cx = js_new<JSContext>(rt);
172 if (!cx)
173 return nullptr;
174
175 if (!cx->cycleDetectorSet.init()) {
176 js_delete(cx);
177 return nullptr;
178 }
179
180 /*
181 * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and
182 * the GC is not running on another thread.
183 */
184 rt->contextList.insertBack(cx);
185
186 /*
187 * If cx is the first context on this runtime, initialize well-known atoms,
188 * keywords, numbers, strings and self-hosted scripts. If one of these
189 * steps should fail, the runtime will be left in a partially initialized
190 * state, with zeroes and nulls stored in the default-initialized remainder
191 * of the struct.
192 */
193 if (!rt->haveCreatedContext) {
194 #ifdef JS_THREADSAFE
195 JS_BeginRequest(cx);
196 #endif
197 bool ok = rt->initializeAtoms(cx);
198 if (ok)
199 ok = rt->initSelfHosting(cx);
200
201 if (ok && !rt->parentRuntime)
202 ok = rt->transformToPermanentAtoms();
203
204 #ifdef JS_THREADSAFE
205 JS_EndRequest(cx);
206 #endif
207 if (!ok) {
208 DestroyContext(cx, DCM_NEW_FAILED);
209 return nullptr;
210 }
211
212 rt->haveCreatedContext = true;
213 }
214
215 JSContextCallback cxCallback = rt->cxCallback;
216 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW, rt->cxCallbackData)) {
217 DestroyContext(cx, DCM_NEW_FAILED);
218 return nullptr;
219 }
220
221 return cx;
222 }
223
224 void
225 js::DestroyContext(JSContext *cx, DestroyContextMode mode)
226 {
227 JSRuntime *rt = cx->runtime();
228 JS_AbortIfWrongThread(rt);
229
230 #ifdef JS_THREADSAFE
231 if (cx->outstandingRequests != 0)
232 MOZ_CRASH();
233 #endif
234
235 #if defined(JSGC_USE_EXACT_ROOTING) && defined(DEBUG)
236 for (int i = 0; i < THING_ROOT_LIMIT; ++i)
237 JS_ASSERT(cx->thingGCRooters[i] == nullptr);
238 #endif
239
240 if (mode != DCM_NEW_FAILED) {
241 if (JSContextCallback cxCallback = rt->cxCallback) {
242 /*
243 * JSCONTEXT_DESTROY callback is not allowed to fail and must
244 * return true.
245 */
246 JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY,
247 rt->cxCallbackData));
248 }
249 }
250
251 cx->remove();
252 bool last = !rt->hasContexts();
253 if (last) {
254 /*
255 * Dump remaining type inference results while we still have a context.
256 * This printing depends on atoms still existing.
257 */
258 for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
259 c->types.print(cx, false);
260 }
261 if (mode == DCM_FORCE_GC) {
262 JS_ASSERT(!rt->isHeapBusy());
263 JS::PrepareForFullGC(rt);
264 GC(rt, GC_NORMAL, JS::gcreason::DESTROY_CONTEXT);
265 }
266 js_delete_poison(cx);
267 }
268
269 bool
270 AutoResolving::alreadyStartedSlow() const
271 {
272 JS_ASSERT(link);
273 AutoResolving *cursor = link;
274 do {
275 JS_ASSERT(this != cursor);
276 if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
277 return true;
278 } while (!!(cursor = cursor->link));
279 return false;
280 }
281
282 static void
283 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
284 JSErrorCallback callback, void *userRef)
285 {
286 /*
287 * Check the error report, and set a JavaScript-catchable exception
288 * if the error is defined to have an associated exception. If an
289 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
290 * on the error report, and exception-aware hosts should ignore it.
291 */
292 JS_ASSERT(reportp);
293 if ((!callback || callback == js_GetErrorMessage) &&
294 reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
295 {
296 reportp->flags |= JSREPORT_EXCEPTION;
297 }
298
299 /*
300 * Call the error reporter only if an exception wasn't raised.
301 *
302 * If an exception was raised, then we call the debugErrorHook
303 * (if present) to give it a chance to see the error before it
304 * propagates out of scope. This is needed for compatibility
305 * with the old scheme.
306 */
307 if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp, callback, userRef)) {
308 if (message)
309 CallErrorReporter(cx, message, reportp);
310 } else if (JSDebugErrorHook hook = cx->runtime()->debugHooks.debugErrorHook) {
311 /*
312 * If we've already chewed up all the C stack, don't call into the
313 * error reporter since this may trigger an infinite recursion where
314 * the reporter triggers an over-recursion.
315 */
316 int stackDummy;
317 if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
318 return;
319
320 if (cx->errorReporter)
321 hook(cx, message, reportp, cx->runtime()->debugHooks.debugErrorHookData);
322 }
323 }
324
325 /*
326 * The given JSErrorReport object have been zeroed and must not outlive
327 * cx->fp() (otherwise report->originPrincipals may become invalid).
328 */
329 static void
330 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
331 {
332 /*
333 * Walk stack until we find a frame that is associated with a non-builtin
334 * rather than a builtin frame.
335 */
336 NonBuiltinFrameIter iter(cx);
337 if (iter.done())
338 return;
339
340 report->filename = iter.scriptFilename();
341 report->lineno = iter.computeLine(&report->column);
342 report->originPrincipals = iter.originPrincipals();
343 }
344
345 /*
346 * Since memory has been exhausted, avoid the normal error-handling path which
347 * allocates an error object, report and callstack. If code is running, simply
348 * throw the static atom "out of memory". If code is not running, call the
349 * error reporter directly.
350 *
351 * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does
352 * not occur, so GC must be avoided or suppressed.
353 */
354 void
355 js_ReportOutOfMemory(ThreadSafeContext *cxArg)
356 {
357 #ifdef JS_MORE_DETERMINISTIC
358 /*
359 * OOMs are non-deterministic, especially across different execution modes
360 * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
361 * so that the fuzzers can detect this.
362 */
363 fprintf(stderr, "js_ReportOutOfMemory called\n");
364 #endif
365
366 if (cxArg->isForkJoinContext()) {
367 cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
368 return;
369 }
370
371 if (!cxArg->isJSContext())
372 return;
373
374 JSContext *cx = cxArg->asJSContext();
375 cx->runtime()->hadOutOfMemory = true;
376
377 /* Report the oom. */
378 if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) {
379 AutoSuppressGC suppressGC(cx);
380 oomCallback(cx);
381 }
382
383 if (JS_IsRunning(cx)) {
384 cx->setPendingException(StringValue(cx->names().outOfMemory));
385 return;
386 }
387
388 /* Get the message for this error, but we don't expand any arguments. */
389 const JSErrorFormatString *efs =
390 js_GetLocalizedErrorMessage(cx, nullptr, nullptr, JSMSG_OUT_OF_MEMORY);
391 const char *msg = efs ? efs->format : "Out of memory";
392
393 /* Fill out the report, but don't do anything that requires allocation. */
394 JSErrorReport report;
395 PodZero(&report);
396 report.flags = JSREPORT_ERROR;
397 report.errorNumber = JSMSG_OUT_OF_MEMORY;
398 PopulateReportBlame(cx, &report);
399
400 /* Report the error. */
401 if (JSErrorReporter onError = cx->errorReporter) {
402 AutoSuppressGC suppressGC(cx);
403 onError(cx, msg, &report);
404 }
405
406 /*
407 * We would like to enforce the invariant that any exception reported
408 * during an OOM situation does not require wrapping. Besides avoiding
409 * allocation when memory is low, this reduces the number of places where
410 * we might need to GC.
411 *
412 * When JS code is running, we set the pending exception to an atom, which
413 * does not need wrapping. If no JS code is running, no exception should be
414 * set at all.
415 */
416 JS_ASSERT(!cx->isExceptionPending());
417 }
418
419 JS_FRIEND_API(void)
420 js_ReportOverRecursed(JSContext *maybecx)
421 {
422 #ifdef JS_MORE_DETERMINISTIC
423 /*
424 * We cannot make stack depth deterministic across different
425 * implementations (e.g. JIT vs. interpreter will differ in
426 * their maximum stack depth).
427 * However, we can detect externally when we hit the maximum
428 * stack depth which is useful for external testing programs
429 * like fuzzers.
430 */
431 fprintf(stderr, "js_ReportOverRecursed called\n");
432 #endif
433 if (maybecx)
434 JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr, JSMSG_OVER_RECURSED);
435 }
436
437 void
438 js_ReportOverRecursed(ThreadSafeContext *cx)
439 {
440 if (cx->isJSContext())
441 js_ReportOverRecursed(cx->asJSContext());
442 else if (cx->isExclusiveContext())
443 cx->asExclusiveContext()->addPendingOverRecursed();
444 }
445
446 void
447 js_ReportAllocationOverflow(ThreadSafeContext *cxArg)
448 {
449 if (!cxArg)
450 return;
451
452 if (cxArg->isForkJoinContext()) {
453 cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
454 return;
455 }
456
457 if (!cxArg->isJSContext())
458 return;
459 JSContext *cx = cxArg->asJSContext();
460
461 AutoSuppressGC suppressGC(cx);
462 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
463 }
464
465 /*
466 * Given flags and the state of cx, decide whether we should report an
467 * error, a warning, or just continue execution normally. Return
468 * true if we should continue normally, without reporting anything;
469 * otherwise, adjust *flags as appropriate and return false.
470 */
471 static bool
472 checkReportFlags(JSContext *cx, unsigned *flags)
473 {
474 if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
475 /*
476 * Error in strict code; warning with extra warnings option; okay
477 * otherwise. We assume that if the top frame is a native, then it is
478 * strict if the nearest scripted frame is strict, see bug 536306.
479 */
480 JSScript *script = cx->currentScript();
481 if (script && script->strict())
482 *flags &= ~JSREPORT_WARNING;
483 else if (cx->options().extraWarnings())
484 *flags |= JSREPORT_WARNING;
485 else
486 return true;
487 } else if (JSREPORT_IS_STRICT(*flags)) {
488 /* Warning/error only when JSOPTION_STRICT is set. */
489 if (!cx->options().extraWarnings())
490 return true;
491 }
492
493 /* Warnings become errors when JSOPTION_WERROR is set. */
494 if (JSREPORT_IS_WARNING(*flags) && cx->options().werror())
495 *flags &= ~JSREPORT_WARNING;
496
497 return false;
498 }
499
500 bool
501 js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap)
502 {
503 char *message;
504 jschar *ucmessage;
505 size_t messagelen;
506 JSErrorReport report;
507 bool warning;
508
509 if (checkReportFlags(cx, &flags))
510 return true;
511
512 message = JS_vsmprintf(format, ap);
513 if (!message)
514 return false;
515 messagelen = strlen(message);
516
517 PodZero(&report);
518 report.flags = flags;
519 report.errorNumber = JSMSG_USER_DEFINED_ERROR;
520 report.ucmessage = ucmessage = InflateString(cx, message, &messagelen);
521 PopulateReportBlame(cx, &report);
522
523 warning = JSREPORT_IS_WARNING(report.flags);
524
525 ReportError(cx, message, &report, nullptr, nullptr);
526 js_free(message);
527 js_free(ucmessage);
528 return warning;
529 }
530
531 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
532 void
533 js::ReportUsageError(JSContext *cx, HandleObject callee, const char *msg)
534 {
535 const char *usageStr = "usage";
536 PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
537 RootedId id(cx, NameToId(usageAtom));
538 DebugOnly<Shape *> shape = static_cast<Shape *>(callee->nativeLookup(cx, id));
539 JS_ASSERT(!shape->configurable());
540 JS_ASSERT(!shape->writable());
541 JS_ASSERT(shape->hasDefaultGetter());
542
543 RootedValue usage(cx);
544 if (!JS_LookupProperty(cx, callee, "usage", &usage))
545 return;
546
547 if (JSVAL_IS_VOID(usage)) {
548 JS_ReportError(cx, "%s", msg);
549 } else {
550 JSString *str = JSVAL_TO_STRING(usage);
551 JS::Anchor<JSString *> a_str(str);
552 const jschar *chars = JS_GetStringCharsZ(cx, str);
553 if (!chars)
554 return;
555 JS_ReportError(cx, "%s. Usage: %hs", msg, chars);
556 }
557 }
558
559 bool
560 js::PrintError(JSContext *cx, FILE *file, const char *message, JSErrorReport *report,
561 bool reportWarnings)
562 {
563 if (!report) {
564 fprintf(file, "%s\n", message);
565 fflush(file);
566 return false;
567 }
568
569 /* Conditionally ignore reported warnings. */
570 if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
571 return false;
572
573 char *prefix = nullptr;
574 if (report->filename)
575 prefix = JS_smprintf("%s:", report->filename);
576 if (report->lineno) {
577 char *tmp = prefix;
578 prefix = JS_smprintf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column);
579 JS_free(cx, tmp);
580 }
581 if (JSREPORT_IS_WARNING(report->flags)) {
582 char *tmp = prefix;
583 prefix = JS_smprintf("%s%swarning: ",
584 tmp ? tmp : "",
585 JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
586 JS_free(cx, tmp);
587 }
588
589 /* embedded newlines -- argh! */
590 const char *ctmp;
591 while ((ctmp = strchr(message, '\n')) != 0) {
592 ctmp++;
593 if (prefix)
594 fputs(prefix, file);
595 fwrite(message, 1, ctmp - message, file);
596 message = ctmp;
597 }
598
599 /* If there were no filename or lineno, the prefix might be empty */
600 if (prefix)
601 fputs(prefix, file);
602 fputs(message, file);
603
604 if (report->linebuf) {
605 /* report->linebuf usually ends with a newline. */
606 int n = strlen(report->linebuf);
607 fprintf(file, ":\n%s%s%s%s",
608 prefix,
609 report->linebuf,
610 (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
611 prefix);
612 n = report->tokenptr - report->linebuf;
613 for (int i = 0, j = 0; i < n; i++) {
614 if (report->linebuf[i] == '\t') {
615 for (int k = (j + 8) & ~7; j < k; j++) {
616 fputc('.', file);
617 }
618 continue;
619 }
620 fputc('.', file);
621 j++;
622 }
623 fputc('^', file);
624 }
625 fputc('\n', file);
626 fflush(file);
627 JS_free(cx, prefix);
628 return true;
629 }
630
631 char *
632 js_strdup(ExclusiveContext *cx, const char *s)
633 {
634 size_t n = strlen(s) + 1;
635 void *p = cx->malloc_(n);
636 if (!p)
637 return nullptr;
638 return (char *)js_memcpy(p, s, n);
639 }
640
641 /*
642 * The arguments from ap need to be packaged up into an array and stored
643 * into the report struct.
644 *
645 * The format string addressed by the error number may contain operands
646 * identified by the format {N}, where N is a decimal digit. Each of these
647 * is to be replaced by the Nth argument from the va_list. The complete
648 * message is placed into reportp->ucmessage converted to a JSString.
649 *
650 * Returns true if the expansion succeeds (can fail if out of memory).
651 */
652 bool
653 js_ExpandErrorArguments(ExclusiveContext *cx, JSErrorCallback callback,
654 void *userRef, const unsigned errorNumber,
655 char **messagep, JSErrorReport *reportp,
656 ErrorArgumentsType argumentsType, va_list ap)
657 {
658 const JSErrorFormatString *efs;
659 int i;
660 int argCount;
661 bool messageArgsPassed = !!reportp->messageArgs;
662
663 *messagep = nullptr;
664
665 /* Most calls supply js_GetErrorMessage; if this is so, assume nullptr. */
666 if (!callback || callback == js_GetErrorMessage)
667 efs = js_GetLocalizedErrorMessage(cx, userRef, nullptr, errorNumber);
668 else
669 efs = callback(userRef, nullptr, errorNumber);
670 if (efs) {
671 reportp->exnType = efs->exnType;
672
673 size_t totalArgsLength = 0;
674 size_t argLengths[10]; /* only {0} thru {9} supported */
675 argCount = efs->argCount;
676 JS_ASSERT(argCount <= 10);
677 if (argCount > 0) {
678 /*
679 * Gather the arguments into an array, and accumulate
680 * their sizes. We allocate 1 more than necessary and
681 * null it out to act as the caboose when we free the
682 * pointers later.
683 */
684 if (messageArgsPassed) {
685 JS_ASSERT(!reportp->messageArgs[argCount]);
686 } else {
687 reportp->messageArgs = cx->pod_malloc<const jschar*>(argCount + 1);
688 if (!reportp->messageArgs)
689 return false;
690 /* nullptr-terminate for easy copying. */
691 reportp->messageArgs[argCount] = nullptr;
692 }
693 for (i = 0; i < argCount; i++) {
694 if (messageArgsPassed) {
695 /* Do nothing. */
696 } else if (argumentsType == ArgumentsAreASCII) {
697 char *charArg = va_arg(ap, char *);
698 size_t charArgLength = strlen(charArg);
699 reportp->messageArgs[i] = InflateString(cx, charArg, &charArgLength);
700 if (!reportp->messageArgs[i])
701 goto error;
702 } else {
703 reportp->messageArgs[i] = va_arg(ap, jschar *);
704 }
705 argLengths[i] = js_strlen(reportp->messageArgs[i]);
706 totalArgsLength += argLengths[i];
707 }
708 }
709 /*
710 * Parse the error format, substituting the argument X
711 * for {X} in the format.
712 */
713 if (argCount > 0) {
714 if (efs->format) {
715 jschar *buffer, *fmt, *out;
716 int expandedArgs = 0;
717 size_t expandedLength;
718 size_t len = strlen(efs->format);
719
720 buffer = fmt = InflateString(cx, efs->format, &len);
721 if (!buffer)
722 goto error;
723 expandedLength = len
724 - (3 * argCount) /* exclude the {n} */
725 + totalArgsLength;
726
727 /*
728 * Note - the above calculation assumes that each argument
729 * is used once and only once in the expansion !!!
730 */
731 reportp->ucmessage = out = cx->pod_malloc<jschar>(expandedLength + 1);
732 if (!out) {
733 js_free(buffer);
734 goto error;
735 }
736 while (*fmt) {
737 if (*fmt == '{') {
738 if (isdigit(fmt[1])) {
739 int d = JS7_UNDEC(fmt[1]);
740 JS_ASSERT(d < argCount);
741 js_strncpy(out, reportp->messageArgs[d],
742 argLengths[d]);
743 out += argLengths[d];
744 fmt += 3;
745 expandedArgs++;
746 continue;
747 }
748 }
749 *out++ = *fmt++;
750 }
751 JS_ASSERT(expandedArgs == argCount);
752 *out = 0;
753 js_free(buffer);
754 TwoByteChars ucmsg(reportp->ucmessage,
755 PointerRangeSize(static_cast<const jschar *>(reportp->ucmessage),
756 static_cast<const jschar *>(out)));
757 *messagep = LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str();
758 if (!*messagep)
759 goto error;
760 }
761 } else {
762 /* Non-null messageArgs should have at least one non-null arg. */
763 JS_ASSERT(!reportp->messageArgs);
764 /*
765 * Zero arguments: the format string (if it exists) is the
766 * entire message.
767 */
768 if (efs->format) {
769 size_t len;
770 *messagep = js_strdup(cx, efs->format);
771 if (!*messagep)
772 goto error;
773 len = strlen(*messagep);
774 reportp->ucmessage = InflateString(cx, *messagep, &len);
775 if (!reportp->ucmessage)
776 goto error;
777 }
778 }
779 }
780 if (*messagep == nullptr) {
781 /* where's the right place for this ??? */
782 const char *defaultErrorMessage
783 = "No error message available for error number %d";
784 size_t nbytes = strlen(defaultErrorMessage) + 16;
785 *messagep = cx->pod_malloc<char>(nbytes);
786 if (!*messagep)
787 goto error;
788 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
789 }
790 return true;
791
792 error:
793 if (!messageArgsPassed && reportp->messageArgs) {
794 /* free the arguments only if we allocated them */
795 if (argumentsType == ArgumentsAreASCII) {
796 i = 0;
797 while (reportp->messageArgs[i])
798 js_free((void *)reportp->messageArgs[i++]);
799 }
800 js_free((void *)reportp->messageArgs);
801 reportp->messageArgs = nullptr;
802 }
803 if (reportp->ucmessage) {
804 js_free((void *)reportp->ucmessage);
805 reportp->ucmessage = nullptr;
806 }
807 if (*messagep) {
808 js_free((void *)*messagep);
809 *messagep = nullptr;
810 }
811 return false;
812 }
813
814 bool
815 js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,
816 void *userRef, const unsigned errorNumber,
817 ErrorArgumentsType argumentsType, va_list ap)
818 {
819 JSErrorReport report;
820 char *message;
821 bool warning;
822
823 if (checkReportFlags(cx, &flags))
824 return true;
825 warning = JSREPORT_IS_WARNING(flags);
826
827 PodZero(&report);
828 report.flags = flags;
829 report.errorNumber = errorNumber;
830 PopulateReportBlame(cx, &report);
831
832 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
833 &message, &report, argumentsType, ap)) {
834 return false;
835 }
836
837 ReportError(cx, message, &report, callback, userRef);
838
839 js_free(message);
840 if (report.messageArgs) {
841 /*
842 * js_ExpandErrorArguments owns its messageArgs only if it had to
843 * inflate the arguments (from regular |char *|s).
844 */
845 if (argumentsType == ArgumentsAreASCII) {
846 int i = 0;
847 while (report.messageArgs[i])
848 js_free((void *)report.messageArgs[i++]);
849 }
850 js_free((void *)report.messageArgs);
851 }
852 js_free((void *)report.ucmessage);
853
854 return warning;
855 }
856
857 bool
858 js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback,
859 void *userRef, const unsigned errorNumber,
860 const jschar **args)
861 {
862 if (checkReportFlags(cx, &flags))
863 return true;
864 bool warning = JSREPORT_IS_WARNING(flags);
865
866 JSErrorReport report;
867 PodZero(&report);
868 report.flags = flags;
869 report.errorNumber = errorNumber;
870 PopulateReportBlame(cx, &report);
871 report.messageArgs = args;
872
873 char *message;
874 va_list dummy;
875 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
876 &message, &report, ArgumentsAreUnicode, dummy)) {
877 return false;
878 }
879
880 ReportError(cx, message, &report, callback, userRef);
881
882 js_free(message);
883 js_free((void *)report.ucmessage);
884
885 return warning;
886 }
887
888 void
889 js::CallErrorReporter(JSContext *cx, const char *message, JSErrorReport *reportp)
890 {
891 JS_ASSERT(message);
892 JS_ASSERT(reportp);
893
894 // If debugErrorHook is present, give it a chance to veto sending the error
895 // on to the regular ErrorReporter.
896 if (cx->errorReporter) {
897 JSDebugErrorHook hook = cx->runtime()->debugHooks.debugErrorHook;
898 if (hook && !hook(cx, message, reportp, cx->runtime()->debugHooks.debugErrorHookData))
899 return;
900 }
901
902 if (JSErrorReporter onError = cx->errorReporter)
903 onError(cx, message, reportp);
904 }
905
906 void
907 js_ReportIsNotDefined(JSContext *cx, const char *name)
908 {
909 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, name);
910 }
911
912 bool
913 js_ReportIsNullOrUndefined(JSContext *cx, int spindex, HandleValue v,
914 HandleString fallback)
915 {
916 char *bytes;
917 bool ok;
918
919 bytes = DecompileValueGenerator(cx, spindex, v, fallback);
920 if (!bytes)
921 return false;
922
923 if (strcmp(bytes, js_undefined_str) == 0 ||
924 strcmp(bytes, js_null_str) == 0) {
925 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
926 js_GetErrorMessage, nullptr,
927 JSMSG_NO_PROPERTIES, bytes,
928 nullptr, nullptr);
929 } else if (v.isUndefined()) {
930 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
931 js_GetErrorMessage, nullptr,
932 JSMSG_UNEXPECTED_TYPE, bytes,
933 js_undefined_str, nullptr);
934 } else {
935 JS_ASSERT(v.isNull());
936 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
937 js_GetErrorMessage, nullptr,
938 JSMSG_UNEXPECTED_TYPE, bytes,
939 js_null_str, nullptr);
940 }
941
942 js_free(bytes);
943 return ok;
944 }
945
946 void
947 js_ReportMissingArg(JSContext *cx, HandleValue v, unsigned arg)
948 {
949 char argbuf[11];
950 char *bytes;
951 RootedAtom atom(cx);
952
953 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
954 bytes = nullptr;
955 if (IsFunctionObject(v)) {
956 atom = v.toObject().as<JSFunction>().atom();
957 bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
958 v, atom);
959 if (!bytes)
960 return;
961 }
962 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
963 JSMSG_MISSING_FUN_ARG, argbuf,
964 bytes ? bytes : "");
965 js_free(bytes);
966 }
967
968 bool
969 js_ReportValueErrorFlags(JSContext *cx, unsigned flags, const unsigned errorNumber,
970 int spindex, HandleValue v, HandleString fallback,
971 const char *arg1, const char *arg2)
972 {
973 char *bytes;
974 bool ok;
975
976 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
977 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
978 bytes = DecompileValueGenerator(cx, spindex, v, fallback);
979 if (!bytes)
980 return false;
981
982 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
983 nullptr, errorNumber, bytes, arg1, arg2);
984 js_free(bytes);
985 return ok;
986 }
987
988 const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
989 #define MSG_DEF(name, number, count, exception, format) \
990 { format, count, exception } ,
991 #include "js.msg"
992 #undef MSG_DEF
993 };
994
995 JS_FRIEND_API(const JSErrorFormatString *)
996 js_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber)
997 {
998 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
999 return &js_ErrorFormatString[errorNumber];
1000 return nullptr;
1001 }
1002
1003 bool
1004 js::InvokeInterruptCallback(JSContext *cx)
1005 {
1006 JS_ASSERT_REQUEST_DEPTH(cx);
1007
1008 JSRuntime *rt = cx->runtime();
1009 JS_ASSERT(rt->interrupt);
1010
1011 // Reset the callback counter first, then run GC and yield. If another
1012 // thread is racing us here we will accumulate another callback request
1013 // which will be serviced at the next opportunity.
1014 rt->interrupt = false;
1015
1016 // IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt
1017 // callbacks.
1018 rt->resetJitStackLimit();
1019
1020 js::gc::GCIfNeeded(cx);
1021
1022 #ifdef JS_ION
1023 #ifdef JS_THREADSAFE
1024 rt->interruptPar = false;
1025 #endif
1026
1027 // A worker thread may have requested an interrupt after finishing an Ion
1028 // compilation.
1029 jit::AttachFinishedCompilations(cx);
1030 #endif
1031
1032 // Important: Additional callbacks can occur inside the callback handler
1033 // if it re-enters the JS engine. The embedding must ensure that the
1034 // callback is disconnected before attempting such re-entry.
1035 JSInterruptCallback cb = cx->runtime()->interruptCallback;
1036 if (!cb || cb(cx))
1037 return true;
1038
1039 // No need to set aside any pending exception here: ComputeStackString
1040 // already does that.
1041 Rooted<JSString*> stack(cx, ComputeStackString(cx));
1042 const jschar *chars = stack ? stack->getCharsZ(cx) : nullptr;
1043 if (!chars)
1044 chars = MOZ_UTF16("(stack not available)");
1045 JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
1046 JSMSG_TERMINATED, chars);
1047
1048 return false;
1049 }
1050
1051 bool
1052 js::HandleExecutionInterrupt(JSContext *cx)
1053 {
1054 if (cx->runtime()->interrupt)
1055 return InvokeInterruptCallback(cx);
1056 return true;
1057 }
1058
1059 ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
1060 : ContextFriendFields(rt),
1061 contextKind_(kind),
1062 perThreadData(pt),
1063 allocator_(nullptr)
1064 {
1065 }
1066
1067 bool
1068 ThreadSafeContext::isForkJoinContext() const
1069 {
1070 return contextKind_ == Context_ForkJoin;
1071 }
1072
1073 ForkJoinContext *
1074 ThreadSafeContext::asForkJoinContext()
1075 {
1076 JS_ASSERT(isForkJoinContext());
1077 return reinterpret_cast<ForkJoinContext *>(this);
1078 }
1079
1080 void
1081 ThreadSafeContext::recoverFromOutOfMemory()
1082 {
1083 // If this is not a JSContext, there's nothing to do.
1084 if (JSContext *maybecx = maybeJSContext()) {
1085 if (maybecx->isExceptionPending()) {
1086 MOZ_ASSERT(maybecx->isThrowingOutOfMemory());
1087 maybecx->clearPendingException();
1088 } else {
1089 MOZ_ASSERT(maybecx->runtime()->hadOutOfMemory);
1090 }
1091 }
1092 }
1093
1094 JSContext::JSContext(JSRuntime *rt)
1095 : ExclusiveContext(rt, &rt->mainThread, Context_JS),
1096 throwing(false),
1097 unwrappedException_(UndefinedValue()),
1098 options_(),
1099 reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
1100 resolvingList(nullptr),
1101 generatingError(false),
1102 savedFrameChains_(),
1103 defaultCompartmentObject_(nullptr),
1104 cycleDetectorSet(MOZ_THIS_IN_INITIALIZER_LIST()),
1105 errorReporter(nullptr),
1106 data(nullptr),
1107 data2(nullptr),
1108 #ifdef JS_THREADSAFE
1109 outstandingRequests(0),
1110 #endif
1111 iterValue(MagicValue(JS_NO_ITER_VALUE)),
1112 jitIsBroken(false),
1113 #ifdef MOZ_TRACE_JSCALLS
1114 functionCallback(nullptr),
1115 #endif
1116 innermostGenerator_(nullptr)
1117 {
1118 #ifdef DEBUG
1119 stackIterAssertionEnabled = true;
1120 #endif
1121
1122 JS_ASSERT(static_cast<ContextFriendFields*>(this) ==
1123 ContextFriendFields::get(this));
1124 }
1125
1126 JSContext::~JSContext()
1127 {
1128 /* Free the stuff hanging off of cx. */
1129 JS_ASSERT(!resolvingList);
1130 }
1131
1132 bool
1133 JSContext::getPendingException(MutableHandleValue rval)
1134 {
1135 JS_ASSERT(throwing);
1136 rval.set(unwrappedException_);
1137 if (IsAtomsCompartment(compartment()))
1138 return true;
1139 clearPendingException();
1140 if (!compartment()->wrap(this, rval))
1141 return false;
1142 assertSameCompartment(this, rval);
1143 setPendingException(rval);
1144 return true;
1145 }
1146
1147 bool
1148 JSContext::isThrowingOutOfMemory()
1149 {
1150 return throwing && unwrappedException_ == StringValue(names().outOfMemory);
1151 }
1152
1153 void
1154 JSContext::enterGenerator(JSGenerator *gen)
1155 {
1156 JS_ASSERT(!gen->prevGenerator);
1157 gen->prevGenerator = innermostGenerator_;
1158 innermostGenerator_ = gen;
1159 }
1160
1161 void
1162 JSContext::leaveGenerator(JSGenerator *gen)
1163 {
1164 JS_ASSERT(innermostGenerator_ == gen);
1165 innermostGenerator_ = innermostGenerator_->prevGenerator;
1166 gen->prevGenerator = nullptr;
1167 }
1168
1169
1170 bool
1171 JSContext::runningWithTrustedPrincipals() const
1172 {
1173 return !compartment() || compartment()->principals == runtime()->trustedPrincipals();
1174 }
1175
1176 bool
1177 JSContext::saveFrameChain()
1178 {
1179 if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_)))
1180 return false;
1181
1182 if (Activation *act = mainThread().activation())
1183 act->saveFrameChain();
1184
1185 setCompartment(nullptr);
1186 enterCompartmentDepth_ = 0;
1187
1188 return true;
1189 }
1190
1191 void
1192 JSContext::restoreFrameChain()
1193 {
1194 JS_ASSERT(enterCompartmentDepth_ == 0); // We're about to clobber it, and it
1195 // will be wrong forevermore.
1196 SavedFrameChain sfc = savedFrameChains_.popCopy();
1197 setCompartment(sfc.compartment);
1198 enterCompartmentDepth_ = sfc.enterCompartmentCount;
1199
1200 if (Activation *act = mainThread().activation())
1201 act->restoreFrameChain();
1202 }
1203
1204 bool
1205 JSContext::currentlyRunning() const
1206 {
1207 for (ActivationIterator iter(runtime()); !iter.done(); ++iter) {
1208 if (iter->cx() == this) {
1209 if (iter->hasSavedFrameChain())
1210 return false;
1211 return true;
1212 }
1213 }
1214
1215 return false;
1216 }
1217
1218 static bool
1219 ComputeIsJITBroken()
1220 {
1221 #if !defined(ANDROID) || defined(GONK)
1222 return false;
1223 #else // ANDROID
1224 if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
1225 return false;
1226 }
1227
1228 std::string line;
1229
1230 // Check for the known-bad kernel version (2.6.29).
1231 std::ifstream osrelease("/proc/sys/kernel/osrelease");
1232 std::getline(osrelease, line);
1233 __android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'",
1234 line.c_str());
1235
1236 if (line.npos == line.find("2.6.29")) {
1237 // We're using something other than 2.6.29, so the JITs should work.
1238 __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken");
1239 return false;
1240 }
1241
1242 // We're using 2.6.29, and this causes trouble with the JITs on i9000.
1243 line = "";
1244 bool broken = false;
1245 std::ifstream cpuinfo("/proc/cpuinfo");
1246 do {
1247 if (0 == line.find("Hardware")) {
1248 static const char* const blacklist[] = {
1249 "SCH-I400", // Samsung Continuum
1250 "SGH-T959", // Samsung i9000, Vibrant device
1251 "SGH-I897", // Samsung i9000, Captivate device
1252 "SCH-I500", // Samsung i9000, Fascinate device
1253 "SPH-D700", // Samsung i9000, Epic device
1254 "GT-I9000", // Samsung i9000, UK/Europe device
1255 nullptr
1256 };
1257 for (const char* const* hw = &blacklist[0]; *hw; ++hw) {
1258 if (line.npos != line.find(*hw)) {
1259 __android_log_print(ANDROID_LOG_INFO, "Gecko",
1260 "Blacklisted device `%s'", *hw);
1261 broken = true;
1262 break;
1263 }
1264 }
1265 break;
1266 }
1267 std::getline(cpuinfo, line);
1268 } while(!cpuinfo.fail() && !cpuinfo.eof());
1269
1270 __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken",
1271 broken ? "" : "not ");
1272
1273 return broken;
1274 #endif // ifndef ANDROID
1275 }
1276
1277 static bool
1278 IsJITBrokenHere()
1279 {
1280 static bool computedIsBroken = false;
1281 static bool isBroken = false;
1282 if (!computedIsBroken) {
1283 isBroken = ComputeIsJITBroken();
1284 computedIsBroken = true;
1285 }
1286 return isBroken;
1287 }
1288
1289 void
1290 JSContext::updateJITEnabled()
1291 {
1292 jitIsBroken = IsJITBrokenHere();
1293 }
1294
1295 size_t
1296 JSContext::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1297 {
1298 /*
1299 * There are other JSContext members that could be measured; the following
1300 * ones have been found by DMD to be worth measuring. More stuff may be
1301 * added later.
1302 */
1303 return mallocSizeOf(this) + cycleDetectorSet.sizeOfExcludingThis(mallocSizeOf);
1304 }
1305
1306 void
1307 JSContext::mark(JSTracer *trc)
1308 {
1309 /* Stack frames and slots are traced by StackSpace::mark. */
1310
1311 /* Mark other roots-by-definition in the JSContext. */
1312 if (defaultCompartmentObject_)
1313 MarkObjectRoot(trc, &defaultCompartmentObject_, "default compartment object");
1314 if (isExceptionPending())
1315 MarkValueRoot(trc, &unwrappedException_, "unwrapped exception");
1316
1317 TraceCycleDetectionSet(trc, cycleDetectorSet);
1318
1319 MarkValueRoot(trc, &iterValue, "iterValue");
1320 }
1321
1322 void *
1323 ThreadSafeContext::stackLimitAddressForJitCode(StackKind kind)
1324 {
1325 #ifdef JS_ARM_SIMULATOR
1326 return runtime_->mainThread.addressOfSimulatorStackLimit();
1327 #endif
1328 return stackLimitAddress(kind);
1329 }
1330
1331 JSVersion
1332 JSContext::findVersion() const
1333 {
1334 if (JSScript *script = currentScript(nullptr, ALLOW_CROSS_COMPARTMENT))
1335 return script->getVersion();
1336
1337 if (compartment() && compartment()->options().version() != JSVERSION_UNKNOWN)
1338 return compartment()->options().version();
1339
1340 return runtime()->defaultVersion();
1341 }
1342
1343 #if defined JS_THREADSAFE && defined DEBUG
1344
1345 JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
1346 : cx(cx)
1347 {
1348 JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
1349 JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1350 cx->runtime()->checkRequestDepth++;
1351 }
1352
1353 JS::AutoCheckRequestDepth::AutoCheckRequestDepth(ContextFriendFields *cxArg)
1354 : cx(static_cast<ThreadSafeContext *>(cxArg)->maybeJSContext())
1355 {
1356 if (cx) {
1357 JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
1358 JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1359 cx->runtime()->checkRequestDepth++;
1360 }
1361 }
1362
1363 JS::AutoCheckRequestDepth::~AutoCheckRequestDepth()
1364 {
1365 if (cx) {
1366 JS_ASSERT(cx->runtime()->checkRequestDepth != 0);
1367 cx->runtime()->checkRequestDepth--;
1368 }
1369 }
1370
1371 #endif
1372
1373 #ifdef JS_CRASH_DIAGNOSTICS
1374 void CompartmentChecker::check(InterpreterFrame *fp)
1375 {
1376 if (fp)
1377 check(fp->scopeChain());
1378 }
1379
1380 void CompartmentChecker::check(AbstractFramePtr frame)
1381 {
1382 if (frame)
1383 check(frame.scopeChain());
1384 }
1385 #endif
1386
1387 void
1388 js::CrashAtUnhandlableOOM(const char *reason)
1389 {
1390 char msgbuf[1024];
1391 JS_snprintf(msgbuf, sizeof(msgbuf), "[unhandlable oom] %s", reason);
1392 MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
1393 MOZ_CRASH();
1394 }

mercurial