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
41 #include "gc/Marking.h"
42 #include "jit/Ion.h"
43 #include "js/CharacterEncoding.h"
44 #include "vm/HelperThreads.h"
45 #include "vm/Shape.h"
46
47 #include "jsobjinlines.h"
48 #include "jsscriptinlines.h"
49
50 #include "vm/Stack-inl.h"
51
52 using namespace js;
53 using namespace js::gc;
54
55 using mozilla::DebugOnly;
56 using mozilla::PodArrayZero;
57 using mozilla::PointerRangeSize;
58 using mozilla::UniquePtr;
59
60 bool
init()61 js::AutoCycleDetector::init()
62 {
63 AutoCycleDetector::Set& set = cx->cycleDetectorSet;
64 hashsetAddPointer = set.lookupForAdd(obj);
65 if (!hashsetAddPointer) {
66 if (!set.add(hashsetAddPointer, obj))
67 return false;
68 cyclic = false;
69 hashsetGenerationAtInit = set.generation();
70 }
71 return true;
72 }
73
~AutoCycleDetector()74 js::AutoCycleDetector::~AutoCycleDetector()
75 {
76 if (!cyclic) {
77 if (hashsetGenerationAtInit == cx->cycleDetectorSet.generation())
78 cx->cycleDetectorSet.remove(hashsetAddPointer);
79 else
80 cx->cycleDetectorSet.remove(obj);
81 }
82 }
83
84 void
TraceCycleDetectionSet(JSTracer * trc,AutoCycleDetector::Set & set)85 js::TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set)
86 {
87 for (AutoCycleDetector::Set::Enum e(set); !e.empty(); e.popFront())
88 TraceRoot(trc, &e.mutableFront(), "cycle detector table entry");
89 }
90
91 JSContext*
NewContext(JSRuntime * rt,size_t stackChunkSize)92 js::NewContext(JSRuntime* rt, size_t stackChunkSize)
93 {
94 JS_AbortIfWrongThread(rt);
95
96 JSContext* cx = js_new<JSContext>(rt);
97 if (!cx)
98 return nullptr;
99
100 if (!cx->cycleDetectorSet.init()) {
101 js_delete(cx);
102 return nullptr;
103 }
104
105 /*
106 * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and
107 * the GC is not running on another thread.
108 */
109 rt->contextList.insertBack(cx);
110
111 /*
112 * If cx is the first context on this runtime, initialize well-known atoms,
113 * keywords, numbers, strings and self-hosted scripts. If one of these
114 * steps should fail, the runtime will be left in a partially initialized
115 * state, with zeroes and nulls stored in the default-initialized remainder
116 * of the struct.
117 */
118 if (!rt->haveCreatedContext) {
119 JS_BeginRequest(cx);
120 bool ok = rt->initializeAtoms(cx);
121 if (ok)
122 ok = rt->initSelfHosting(cx);
123
124 if (ok && !rt->parentRuntime)
125 ok = rt->transformToPermanentAtoms(cx);
126
127 JS_EndRequest(cx);
128
129 if (!ok) {
130 DestroyContext(cx, DCM_NEW_FAILED);
131 return nullptr;
132 }
133
134 rt->haveCreatedContext = true;
135 }
136
137 JSContextCallback cxCallback = rt->cxCallback;
138 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW, rt->cxCallbackData)) {
139 DestroyContext(cx, DCM_NEW_FAILED);
140 return nullptr;
141 }
142
143 return cx;
144 }
145
146 void
DestroyContext(JSContext * cx,DestroyContextMode mode)147 js::DestroyContext(JSContext* cx, DestroyContextMode mode)
148 {
149 JSRuntime* rt = cx->runtime();
150 JS_AbortIfWrongThread(rt);
151
152 if (cx->outstandingRequests != 0)
153 MOZ_CRASH("Attempted to destroy a context while it is in a request.");
154
155 cx->roots.checkNoGCRooters();
156 FinishPersistentRootedChains(cx->roots);
157
158 if (mode != DCM_NEW_FAILED) {
159 if (JSContextCallback cxCallback = rt->cxCallback) {
160 /*
161 * JSCONTEXT_DESTROY callback is not allowed to fail and must
162 * return true.
163 */
164 JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY,
165 rt->cxCallbackData));
166 }
167 }
168
169 cx->remove();
170 bool last = !rt->hasContexts();
171 if (last) {
172 /*
173 * Dump remaining type inference results while we still have a context.
174 * This printing depends on atoms still existing.
175 */
176 for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
177 PrintTypes(cx, c, false);
178 }
179 if (mode == DCM_FORCE_GC) {
180 MOZ_ASSERT(!rt->isHeapBusy());
181 JS::PrepareForFullGC(rt);
182 rt->gc.gc(GC_NORMAL, JS::gcreason::DESTROY_CONTEXT);
183 }
184 js_delete_poison(cx);
185 }
186
187 void
checkNoGCRooters()188 RootLists::checkNoGCRooters() {
189 #ifdef DEBUG
190 for (int i = 0; i < THING_ROOT_LIMIT; ++i)
191 MOZ_ASSERT(stackRoots_[i] == nullptr);
192 #endif
193 }
194
195 bool
alreadyStartedSlow() const196 AutoResolving::alreadyStartedSlow() const
197 {
198 MOZ_ASSERT(link);
199 AutoResolving* cursor = link;
200 do {
201 MOZ_ASSERT(this != cursor);
202 if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
203 return true;
204 } while (!!(cursor = cursor->link));
205 return false;
206 }
207
208 static void
ReportError(JSContext * cx,const char * message,JSErrorReport * reportp,JSErrorCallback callback,void * userRef)209 ReportError(JSContext* cx, const char* message, JSErrorReport* reportp,
210 JSErrorCallback callback, void* userRef)
211 {
212 /*
213 * Check the error report, and set a JavaScript-catchable exception
214 * if the error is defined to have an associated exception. If an
215 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
216 * on the error report, and exception-aware hosts should ignore it.
217 */
218 MOZ_ASSERT(reportp);
219 if ((!callback || callback == GetErrorMessage) &&
220 reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
221 {
222 reportp->flags |= JSREPORT_EXCEPTION;
223 }
224
225 if (cx->options().autoJSAPIOwnsErrorReporting() || JS_IsRunning(cx)) {
226 if (ErrorToException(cx, message, reportp, callback, userRef))
227 return;
228
229 /*
230 * The AutoJSAPI error reporter only allows warnings to be reported so
231 * just ignore this error rather than try to report it.
232 */
233 if (cx->options().autoJSAPIOwnsErrorReporting() && !JSREPORT_IS_WARNING(reportp->flags))
234 return;
235 }
236
237 /*
238 * Call the error reporter only if an exception wasn't raised.
239 */
240 if (message)
241 CallErrorReporter(cx, message, reportp);
242 }
243
244 /*
245 * The given JSErrorReport object have been zeroed and must not outlive
246 * cx->fp() (otherwise owned fields may become invalid).
247 */
248 static void
PopulateReportBlame(JSContext * cx,JSErrorReport * report)249 PopulateReportBlame(JSContext* cx, JSErrorReport* report)
250 {
251 JSCompartment* compartment = cx->compartment();
252 if (!compartment)
253 return;
254
255 /*
256 * Walk stack until we find a frame that is associated with a non-builtin
257 * rather than a builtin frame and which we're allowed to know about.
258 */
259 NonBuiltinFrameIter iter(cx, compartment->principals());
260 if (iter.done())
261 return;
262
263 report->filename = iter.scriptFilename();
264 report->lineno = iter.computeLine(&report->column);
265 // XXX: Make the column 1-based as in other browsers, instead of 0-based
266 // which is how SpiderMonkey stores it internally. This will be
267 // unnecessary once bug 1144340 is fixed.
268 report->column++;
269 report->isMuted = iter.mutedErrors();
270 }
271
272 /*
273 * Since memory has been exhausted, avoid the normal error-handling path which
274 * allocates an error object, report and callstack. If code is running, simply
275 * throw the static atom "out of memory". If code is not running, call the
276 * error reporter directly.
277 *
278 * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
279 * not occur, so GC must be avoided or suppressed.
280 */
281 void
ReportOutOfMemory(ExclusiveContext * cxArg)282 js::ReportOutOfMemory(ExclusiveContext* cxArg)
283 {
284 #ifdef JS_MORE_DETERMINISTIC
285 /*
286 * OOMs are non-deterministic, especially across different execution modes
287 * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
288 * so that the fuzzers can detect this.
289 */
290 fprintf(stderr, "ReportOutOfMemory called\n");
291 #endif
292
293 if (!cxArg->isJSContext())
294 return;
295
296 JSContext* cx = cxArg->asJSContext();
297 cx->runtime()->hadOutOfMemory = true;
298
299 /* Report the oom. */
300 if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) {
301 AutoSuppressGC suppressGC(cx);
302 oomCallback(cx, cx->runtime()->oomCallbackData);
303 }
304
305 if (JS_IsRunning(cx)) {
306 cx->setPendingException(StringValue(cx->names().outOfMemory));
307 return;
308 }
309
310 /* Get the message for this error, but we don't expand any arguments. */
311 const JSErrorFormatString* efs = GetErrorMessage(nullptr, JSMSG_OUT_OF_MEMORY);
312 const char* msg = efs ? efs->format : "Out of memory";
313
314 /* Fill out the report, but don't do anything that requires allocation. */
315 JSErrorReport report;
316 report.flags = JSREPORT_ERROR;
317 report.errorNumber = JSMSG_OUT_OF_MEMORY;
318 PopulateReportBlame(cx, &report);
319
320 /* Report the error. */
321 if (JSErrorReporter onError = cx->runtime()->errorReporter) {
322 AutoSuppressGC suppressGC(cx);
323 onError(cx, msg, &report);
324 }
325
326 /*
327 * We would like to enforce the invariant that any exception reported
328 * during an OOM situation does not require wrapping. Besides avoiding
329 * allocation when memory is low, this reduces the number of places where
330 * we might need to GC.
331 *
332 * When JS code is running, we set the pending exception to an atom, which
333 * does not need wrapping. If no JS code is running, no exception should be
334 * set at all.
335 */
336 MOZ_ASSERT(!cx->isExceptionPending());
337 }
338
JS_FRIEND_API(void)339 JS_FRIEND_API(void)
340 js::ReportOverRecursed(JSContext* maybecx)
341 {
342 #ifdef JS_MORE_DETERMINISTIC
343 /*
344 * We cannot make stack depth deterministic across different
345 * implementations (e.g. JIT vs. interpreter will differ in
346 * their maximum stack depth).
347 * However, we can detect externally when we hit the maximum
348 * stack depth which is useful for external testing programs
349 * like fuzzers.
350 */
351 fprintf(stderr, "ReportOverRecursed called\n");
352 #endif
353 if (maybecx) {
354 JS_ReportErrorNumber(maybecx, GetErrorMessage, nullptr, JSMSG_OVER_RECURSED);
355 maybecx->overRecursed_ = true;
356 }
357 }
358
359 void
ReportOverRecursed(ExclusiveContext * cx)360 js::ReportOverRecursed(ExclusiveContext* cx)
361 {
362 if (cx->isJSContext())
363 ReportOverRecursed(cx->asJSContext());
364 else
365 cx->addPendingOverRecursed();
366 }
367
368 void
ReportAllocationOverflow(ExclusiveContext * cxArg)369 js::ReportAllocationOverflow(ExclusiveContext* cxArg)
370 {
371 if (!cxArg)
372 return;
373
374 if (!cxArg->isJSContext())
375 return;
376 JSContext* cx = cxArg->asJSContext();
377
378 AutoSuppressGC suppressGC(cx);
379 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
380 }
381
382 /*
383 * Given flags and the state of cx, decide whether we should report an
384 * error, a warning, or just continue execution normally. Return
385 * true if we should continue normally, without reporting anything;
386 * otherwise, adjust *flags as appropriate and return false.
387 */
388 static bool
checkReportFlags(JSContext * cx,unsigned * flags)389 checkReportFlags(JSContext* cx, unsigned* flags)
390 {
391 if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
392 /*
393 * Error in strict code; warning with extra warnings option; okay
394 * otherwise. We assume that if the top frame is a native, then it is
395 * strict if the nearest scripted frame is strict, see bug 536306.
396 */
397 jsbytecode* pc;
398 JSScript* script = cx->currentScript(&pc);
399 if (script && IsCheckStrictOp(JSOp(*pc)))
400 *flags &= ~JSREPORT_WARNING;
401 else if (cx->compartment()->options().extraWarnings(cx))
402 *flags |= JSREPORT_WARNING;
403 else
404 return true;
405 } else if (JSREPORT_IS_STRICT(*flags)) {
406 /* Warning/error only when JSOPTION_STRICT is set. */
407 if (!cx->compartment()->options().extraWarnings(cx))
408 return true;
409 }
410
411 /* Warnings become errors when JSOPTION_WERROR is set. */
412 if (JSREPORT_IS_WARNING(*flags) && cx->runtime()->options().werror())
413 *flags &= ~JSREPORT_WARNING;
414
415 return false;
416 }
417
418 bool
ReportErrorVA(JSContext * cx,unsigned flags,const char * format,va_list ap)419 js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format, va_list ap)
420 {
421 char* message;
422 char16_t* ucmessage;
423 size_t messagelen;
424 JSErrorReport report;
425 bool warning;
426
427 if (checkReportFlags(cx, &flags))
428 return true;
429
430 message = JS_vsmprintf(format, ap);
431 if (!message) {
432 ReportOutOfMemory(cx);
433 return false;
434 }
435 messagelen = strlen(message);
436
437 report.flags = flags;
438 report.errorNumber = JSMSG_USER_DEFINED_ERROR;
439 report.ucmessage = ucmessage = InflateString(cx, message, &messagelen);
440 PopulateReportBlame(cx, &report);
441
442 warning = JSREPORT_IS_WARNING(report.flags);
443
444 ReportError(cx, message, &report, nullptr, nullptr);
445 js_free(message);
446 js_free(ucmessage);
447 return warning;
448 }
449
450 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
451 void
ReportUsageError(JSContext * cx,HandleObject callee,const char * msg)452 js::ReportUsageError(JSContext* cx, HandleObject callee, const char* msg)
453 {
454 const char* usageStr = "usage";
455 PropertyName* usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
456 RootedId id(cx, NameToId(usageAtom));
457 DebugOnly<Shape*> shape = static_cast<Shape*>(callee->as<JSFunction>().lookup(cx, id));
458 MOZ_ASSERT(!shape->configurable());
459 MOZ_ASSERT(!shape->writable());
460 MOZ_ASSERT(shape->hasDefaultGetter());
461
462 RootedValue usage(cx);
463 if (!JS_GetProperty(cx, callee, "usage", &usage))
464 return;
465
466 if (!usage.isString()) {
467 JS_ReportError(cx, "%s", msg);
468 } else {
469 JSString* str = usage.toString();
470 if (!str->ensureFlat(cx))
471 return;
472 AutoStableStringChars chars(cx);
473 if (!chars.initTwoByte(cx, str))
474 return;
475
476 JS_ReportError(cx, "%s. Usage: %hs", msg, chars.twoByteRange().start().get());
477 }
478 }
479
480 bool
PrintError(JSContext * cx,FILE * file,const char * message,JSErrorReport * report,bool reportWarnings)481 js::PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report,
482 bool reportWarnings)
483 {
484 if (!report) {
485 fprintf(file, "%s\n", message);
486 fflush(file);
487 return false;
488 }
489
490 /* Conditionally ignore reported warnings. */
491 if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
492 return false;
493
494 char* prefix = nullptr;
495 if (report->filename)
496 prefix = JS_smprintf("%s:", report->filename);
497 if (report->lineno) {
498 char* tmp = prefix;
499 prefix = JS_smprintf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column);
500 JS_free(cx, tmp);
501 }
502 if (JSREPORT_IS_WARNING(report->flags)) {
503 char* tmp = prefix;
504 prefix = JS_smprintf("%s%swarning: ",
505 tmp ? tmp : "",
506 JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
507 JS_free(cx, tmp);
508 }
509
510 /* embedded newlines -- argh! */
511 const char* ctmp;
512 while ((ctmp = strchr(message, '\n')) != 0) {
513 ctmp++;
514 if (prefix)
515 fputs(prefix, file);
516 fwrite(message, 1, ctmp - message, file);
517 message = ctmp;
518 }
519
520 /* If there were no filename or lineno, the prefix might be empty */
521 if (prefix)
522 fputs(prefix, file);
523 fputs(message, file);
524
525 if (const char16_t* linebuf = report->linebuf()) {
526 size_t n = report->linebufLength();
527
528 fputs(":\n", file);
529 if (prefix)
530 fputs(prefix, file);
531
532 for (size_t i = 0; i < n; i++)
533 fputc(static_cast<char>(linebuf[i]), file);
534
535 // linebuf usually ends with a newline. If not, add one here.
536 if (n == 0 || linebuf[n-1] != '\n')
537 fputc('\n', file);
538
539 if (prefix)
540 fputs(prefix, file);
541
542 n = report->tokenOffset();
543 for (size_t i = 0, j = 0; i < n; i++) {
544 if (linebuf[i] == '\t') {
545 for (size_t k = (j + 8) & ~7; j < k; j++)
546 fputc('.', file);
547 continue;
548 }
549 fputc('.', file);
550 j++;
551 }
552 fputc('^', file);
553 }
554 fputc('\n', file);
555 fflush(file);
556 JS_free(cx, prefix);
557 return true;
558 }
559
560 /*
561 * The arguments from ap need to be packaged up into an array and stored
562 * into the report struct.
563 *
564 * The format string addressed by the error number may contain operands
565 * identified by the format {N}, where N is a decimal digit. Each of these
566 * is to be replaced by the Nth argument from the va_list. The complete
567 * message is placed into reportp->ucmessage converted to a JSString.
568 *
569 * Returns true if the expansion succeeds (can fail if out of memory).
570 */
571 bool
ExpandErrorArgumentsVA(ExclusiveContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,char ** messagep,JSErrorReport * reportp,ErrorArgumentsType argumentsType,va_list ap)572 js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
573 void* userRef, const unsigned errorNumber,
574 char** messagep, JSErrorReport* reportp,
575 ErrorArgumentsType argumentsType, va_list ap)
576 {
577 const JSErrorFormatString* efs;
578 uint16_t argCount;
579 bool messageArgsPassed = !!reportp->messageArgs;
580
581 *messagep = nullptr;
582
583 if (!callback)
584 callback = GetErrorMessage;
585
586 {
587 AutoSuppressGC suppressGC(cx);
588 efs = callback(userRef, errorNumber);
589 }
590
591 if (efs) {
592 reportp->exnType = efs->exnType;
593
594 size_t totalArgsLength = 0;
595 size_t argLengths[JS::MaxNumErrorArguments]; /* only {0} thru {9} supported */
596 argCount = efs->argCount;
597 MOZ_RELEASE_ASSERT(argCount <= JS::MaxNumErrorArguments);
598 if (argCount > 0) {
599 /*
600 * Gather the arguments into an array, and accumulate
601 * their sizes. We allocate 1 more than necessary and
602 * null it out to act as the caboose when we free the
603 * pointers later.
604 */
605 if (messageArgsPassed) {
606 MOZ_ASSERT(!reportp->messageArgs[argCount]);
607 } else {
608 reportp->messageArgs = cx->pod_malloc<const char16_t*>(argCount + 1);
609 if (!reportp->messageArgs)
610 return false;
611 /* nullptr-terminate for easy copying. */
612 reportp->messageArgs[argCount] = nullptr;
613 }
614 for (uint16_t i = 0; i < argCount; i++) {
615 if (messageArgsPassed) {
616 /* Do nothing. */
617 } else if (argumentsType == ArgumentsAreASCII) {
618 char* charArg = va_arg(ap, char*);
619 size_t charArgLength = strlen(charArg);
620 reportp->messageArgs[i] = InflateString(cx, charArg, &charArgLength);
621 if (!reportp->messageArgs[i])
622 goto error;
623 } else {
624 reportp->messageArgs[i] = va_arg(ap, char16_t*);
625 }
626 argLengths[i] = js_strlen(reportp->messageArgs[i]);
627 totalArgsLength += argLengths[i];
628 }
629 }
630 /*
631 * Parse the error format, substituting the argument X
632 * for {X} in the format.
633 */
634 if (argCount > 0) {
635 if (efs->format) {
636 char16_t* buffer;
637 char16_t* fmt;
638 char16_t* out;
639 int expandedArgs = 0;
640 size_t expandedLength;
641 size_t len = strlen(efs->format);
642
643 buffer = fmt = InflateString(cx, efs->format, &len);
644 if (!buffer)
645 goto error;
646 expandedLength = len
647 - (3 * argCount) /* exclude the {n} */
648 + totalArgsLength;
649
650 /*
651 * Note - the above calculation assumes that each argument
652 * is used once and only once in the expansion !!!
653 */
654 reportp->ucmessage = out = cx->pod_malloc<char16_t>(expandedLength + 1);
655 if (!out) {
656 ReportOutOfMemory(cx);
657 js_free(buffer);
658 goto error;
659 }
660 while (*fmt) {
661 if (*fmt == '{') {
662 if (isdigit(fmt[1])) {
663 int d = JS7_UNDEC(fmt[1]);
664 MOZ_RELEASE_ASSERT(d < argCount);
665 js_strncpy(out, reportp->messageArgs[d],
666 argLengths[d]);
667 out += argLengths[d];
668 fmt += 3;
669 expandedArgs++;
670 continue;
671 }
672 }
673 *out++ = *fmt++;
674 }
675 MOZ_ASSERT(expandedArgs == argCount);
676 *out = 0;
677 js_free(buffer);
678 size_t msgLen = PointerRangeSize(static_cast<const char16_t*>(reportp->ucmessage),
679 static_cast<const char16_t*>(out));
680 mozilla::Range<const char16_t> ucmsg(reportp->ucmessage, msgLen);
681 *messagep = JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str();
682 if (!*messagep)
683 goto error;
684 }
685 } else {
686 /* Non-null messageArgs should have at least one non-null arg. */
687 MOZ_ASSERT(!reportp->messageArgs);
688 /*
689 * Zero arguments: the format string (if it exists) is the
690 * entire message.
691 */
692 if (efs->format) {
693 size_t len;
694 *messagep = DuplicateString(cx, efs->format).release();
695 if (!*messagep)
696 goto error;
697 len = strlen(*messagep);
698 reportp->ucmessage = InflateString(cx, *messagep, &len);
699 if (!reportp->ucmessage)
700 goto error;
701 }
702 }
703 }
704 if (*messagep == nullptr) {
705 /* where's the right place for this ??? */
706 const char* defaultErrorMessage
707 = "No error message available for error number %d";
708 size_t nbytes = strlen(defaultErrorMessage) + 16;
709 *messagep = cx->pod_malloc<char>(nbytes);
710 if (!*messagep)
711 goto error;
712 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
713 }
714 return true;
715
716 error:
717 if (!messageArgsPassed && reportp->messageArgs) {
718 /* free the arguments only if we allocated them */
719 if (argumentsType == ArgumentsAreASCII) {
720 uint16_t i = 0;
721 while (reportp->messageArgs[i])
722 js_free((void*)reportp->messageArgs[i++]);
723 }
724 js_free((void*)reportp->messageArgs);
725 reportp->messageArgs = nullptr;
726 }
727 if (reportp->ucmessage) {
728 js_free((void*)reportp->ucmessage);
729 reportp->ucmessage = nullptr;
730 }
731 if (*messagep) {
732 js_free((void*)*messagep);
733 *messagep = nullptr;
734 }
735 return false;
736 }
737
738 bool
ReportErrorNumberVA(JSContext * cx,unsigned flags,JSErrorCallback callback,void * userRef,const unsigned errorNumber,ErrorArgumentsType argumentsType,va_list ap)739 js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
740 void* userRef, const unsigned errorNumber,
741 ErrorArgumentsType argumentsType, va_list ap)
742 {
743 JSErrorReport report;
744 char* message;
745 bool warning;
746
747 if (checkReportFlags(cx, &flags))
748 return true;
749 warning = JSREPORT_IS_WARNING(flags);
750
751 report.flags = flags;
752 report.errorNumber = errorNumber;
753 PopulateReportBlame(cx, &report);
754
755 if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
756 &message, &report, argumentsType, ap)) {
757 return false;
758 }
759
760 ReportError(cx, message, &report, callback, userRef);
761
762 js_free(message);
763 if (report.messageArgs) {
764 /*
765 * ExpandErrorArgumentsVA owns its messageArgs only if it had to
766 * inflate the arguments (from regular |char*|s).
767 */
768 if (argumentsType == ArgumentsAreASCII) {
769 int i = 0;
770 while (report.messageArgs[i])
771 js_free((void*)report.messageArgs[i++]);
772 }
773 js_free((void*)report.messageArgs);
774 }
775 js_free((void*)report.ucmessage);
776
777 return warning;
778 }
779
780 static bool
ExpandErrorArguments(ExclusiveContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,char ** messagep,JSErrorReport * reportp,ErrorArgumentsType argumentsType,...)781 ExpandErrorArguments(ExclusiveContext* cx, JSErrorCallback callback,
782 void* userRef, const unsigned errorNumber,
783 char** messagep, JSErrorReport* reportp,
784 ErrorArgumentsType argumentsType, ...)
785 {
786 va_list ap;
787 va_start(ap, argumentsType);
788 bool expanded = js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
789 messagep, reportp, argumentsType, ap);
790 va_end(ap);
791 return expanded;
792 }
793
794 bool
ReportErrorNumberUCArray(JSContext * cx,unsigned flags,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const char16_t ** args)795 js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
796 void* userRef, const unsigned errorNumber,
797 const char16_t** args)
798 {
799 if (checkReportFlags(cx, &flags))
800 return true;
801 bool warning = JSREPORT_IS_WARNING(flags);
802
803 JSErrorReport report;
804 report.flags = flags;
805 report.errorNumber = errorNumber;
806 PopulateReportBlame(cx, &report);
807 report.messageArgs = args;
808
809 char* message;
810 if (!ExpandErrorArguments(cx, callback, userRef, errorNumber,
811 &message, &report, ArgumentsAreUnicode)) {
812 return false;
813 }
814
815 ReportError(cx, message, &report, callback, userRef);
816
817 js_free(message);
818 js_free((void*)report.ucmessage);
819
820 return warning;
821 }
822
823 void
CallErrorReporter(JSContext * cx,const char * message,JSErrorReport * reportp)824 js::CallErrorReporter(JSContext* cx, const char* message, JSErrorReport* reportp)
825 {
826 MOZ_ASSERT(message);
827 MOZ_ASSERT(reportp);
828
829 if (JSErrorReporter onError = cx->runtime()->errorReporter)
830 onError(cx, message, reportp);
831 }
832
833 bool
ReportIsNotDefined(JSContext * cx,HandleId id)834 js::ReportIsNotDefined(JSContext* cx, HandleId id)
835 {
836 JSAutoByteString printable;
837 if (ValueToPrintable(cx, IdToValue(id), &printable))
838 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.ptr());
839 return false;
840 }
841
842 bool
ReportIsNotDefined(JSContext * cx,HandlePropertyName name)843 js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
844 {
845 RootedId id(cx, NameToId(name));
846 return ReportIsNotDefined(cx, id);
847 }
848
849 bool
ReportIsNullOrUndefined(JSContext * cx,int spindex,HandleValue v,HandleString fallback)850 js::ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v,
851 HandleString fallback)
852 {
853 bool ok;
854
855 UniquePtr<char[], JS::FreePolicy> bytes =
856 DecompileValueGenerator(cx, spindex, v, fallback);
857 if (!bytes)
858 return false;
859
860 if (strcmp(bytes.get(), js_undefined_str) == 0 ||
861 strcmp(bytes.get(), js_null_str) == 0) {
862 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
863 GetErrorMessage, nullptr,
864 JSMSG_NO_PROPERTIES, bytes.get(),
865 nullptr, nullptr);
866 } else if (v.isUndefined()) {
867 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
868 GetErrorMessage, nullptr,
869 JSMSG_UNEXPECTED_TYPE, bytes.get(),
870 js_undefined_str, nullptr);
871 } else {
872 MOZ_ASSERT(v.isNull());
873 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
874 GetErrorMessage, nullptr,
875 JSMSG_UNEXPECTED_TYPE, bytes.get(),
876 js_null_str, nullptr);
877 }
878
879 return ok;
880 }
881
882 void
ReportMissingArg(JSContext * cx,HandleValue v,unsigned arg)883 js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
884 {
885 char argbuf[11];
886 UniquePtr<char[], JS::FreePolicy> bytes;
887 RootedAtom atom(cx);
888
889 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
890 if (IsFunctionObject(v)) {
891 atom = v.toObject().as<JSFunction>().atom();
892 bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, atom);
893 if (!bytes)
894 return;
895 }
896 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
897 JSMSG_MISSING_FUN_ARG, argbuf,
898 bytes ? bytes.get() : "");
899 }
900
901 bool
ReportValueErrorFlags(JSContext * cx,unsigned flags,const unsigned errorNumber,int spindex,HandleValue v,HandleString fallback,const char * arg1,const char * arg2)902 js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
903 int spindex, HandleValue v, HandleString fallback,
904 const char* arg1, const char* arg2)
905 {
906 UniquePtr<char[], JS::FreePolicy> bytes;
907 bool ok;
908
909 MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
910 MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
911 bytes = DecompileValueGenerator(cx, spindex, v, fallback);
912 if (!bytes)
913 return false;
914
915 ok = JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage,
916 nullptr, errorNumber, bytes.get(), arg1, arg2);
917 return ok;
918 }
919
920 const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
921 #define MSG_DEF(name, count, exception, format) \
922 { format, count, exception } ,
923 #include "js.msg"
924 #undef MSG_DEF
925 };
926
JS_FRIEND_API(const JSErrorFormatString *)927 JS_FRIEND_API(const JSErrorFormatString*)
928 js::GetErrorMessage(void* userRef, const unsigned errorNumber)
929 {
930 if (errorNumber > 0 && errorNumber < JSErr_Limit)
931 return &js_ErrorFormatString[errorNumber];
932 return nullptr;
933 }
934
ExclusiveContext(JSRuntime * rt,PerThreadData * pt,ContextKind kind)935 ExclusiveContext::ExclusiveContext(JSRuntime* rt, PerThreadData* pt, ContextKind kind)
936 : ContextFriendFields(rt),
937 helperThread_(nullptr),
938 contextKind_(kind),
939 perThreadData(pt),
940 arenas_(nullptr),
941 enterCompartmentDepth_(0)
942 {
943 }
944
945 void
recoverFromOutOfMemory()946 ExclusiveContext::recoverFromOutOfMemory()
947 {
948 // If this is not a JSContext, there's nothing to do.
949 if (JSContext* maybecx = maybeJSContext()) {
950 if (maybecx->isExceptionPending()) {
951 MOZ_ASSERT(maybecx->isThrowingOutOfMemory());
952 maybecx->clearPendingException();
953 }
954 }
955 }
956
JSContext(JSRuntime * rt)957 JSContext::JSContext(JSRuntime* rt)
958 : ExclusiveContext(rt, &rt->mainThread, Context_JS),
959 throwing(false),
960 unwrappedException_(this),
961 options_(),
962 overRecursed_(false),
963 propagatingForcedReturn_(false),
964 liveVolatileJitFrameIterators_(nullptr),
965 reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
966 resolvingList(nullptr),
967 generatingError(false),
968 savedFrameChains_(),
969 cycleDetectorSet(this),
970 data(nullptr),
971 data2(nullptr),
972 outstandingRequests(0),
973 jitIsBroken(false)
974 {
975 MOZ_ASSERT(static_cast<ContextFriendFields*>(this) ==
976 ContextFriendFields::get(this));
977 }
978
~JSContext()979 JSContext::~JSContext()
980 {
981 /* Free the stuff hanging off of cx. */
982 MOZ_ASSERT(!resolvingList);
983 }
984
985 bool
getPendingException(MutableHandleValue rval)986 JSContext::getPendingException(MutableHandleValue rval)
987 {
988 MOZ_ASSERT(throwing);
989 rval.set(unwrappedException_);
990 if (IsAtomsCompartment(compartment()))
991 return true;
992 bool wasOverRecursed = overRecursed_;
993 clearPendingException();
994 if (!compartment()->wrap(this, rval))
995 return false;
996 assertSameCompartment(this, rval);
997 setPendingException(rval);
998 overRecursed_ = wasOverRecursed;
999 return true;
1000 }
1001
1002 bool
isThrowingOutOfMemory()1003 JSContext::isThrowingOutOfMemory()
1004 {
1005 return throwing && unwrappedException_ == StringValue(names().outOfMemory);
1006 }
1007
1008 bool
isClosingGenerator()1009 JSContext::isClosingGenerator()
1010 {
1011 return throwing && unwrappedException_.isMagic(JS_GENERATOR_CLOSING);
1012 }
1013
1014 bool
saveFrameChain()1015 JSContext::saveFrameChain()
1016 {
1017 if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_)))
1018 return false;
1019
1020 if (Activation* act = runtime()->activation())
1021 act->saveFrameChain();
1022
1023 setCompartment(nullptr);
1024 enterCompartmentDepth_ = 0;
1025
1026 return true;
1027 }
1028
1029 void
restoreFrameChain()1030 JSContext::restoreFrameChain()
1031 {
1032 MOZ_ASSERT(enterCompartmentDepth_ == 0); // We're about to clobber it, and it
1033 // will be wrong forevermore.
1034 SavedFrameChain sfc = savedFrameChains_.popCopy();
1035 setCompartment(sfc.compartment);
1036 enterCompartmentDepth_ = sfc.enterCompartmentCount;
1037
1038 if (Activation* act = runtime()->activation())
1039 act->restoreFrameChain();
1040 }
1041
1042 bool
currentlyRunning() const1043 JSContext::currentlyRunning() const
1044 {
1045 for (ActivationIterator iter(runtime()); !iter.done(); ++iter) {
1046 if (iter->cx() == this) {
1047 if (iter->hasSavedFrameChain())
1048 return false;
1049 return true;
1050 }
1051 }
1052
1053 return false;
1054 }
1055
1056 static bool
ComputeIsJITBroken()1057 ComputeIsJITBroken()
1058 {
1059 #if !defined(ANDROID) || defined(GONK)
1060 return false;
1061 #else // ANDROID
1062 if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
1063 return false;
1064 }
1065
1066 std::string line;
1067
1068 // Check for the known-bad kernel version (2.6.29).
1069 std::ifstream osrelease("/proc/sys/kernel/osrelease");
1070 std::getline(osrelease, line);
1071 __android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'",
1072 line.c_str());
1073
1074 if (line.npos == line.find("2.6.29")) {
1075 // We're using something other than 2.6.29, so the JITs should work.
1076 __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken");
1077 return false;
1078 }
1079
1080 // We're using 2.6.29, and this causes trouble with the JITs on i9000.
1081 line = "";
1082 bool broken = false;
1083 std::ifstream cpuinfo("/proc/cpuinfo");
1084 do {
1085 if (0 == line.find("Hardware")) {
1086 static const char* const blacklist[] = {
1087 "SCH-I400", // Samsung Continuum
1088 "SGH-T959", // Samsung i9000, Vibrant device
1089 "SGH-I897", // Samsung i9000, Captivate device
1090 "SCH-I500", // Samsung i9000, Fascinate device
1091 "SPH-D700", // Samsung i9000, Epic device
1092 "GT-I9000", // Samsung i9000, UK/Europe device
1093 nullptr
1094 };
1095 for (const char* const* hw = &blacklist[0]; *hw; ++hw) {
1096 if (line.npos != line.find(*hw)) {
1097 __android_log_print(ANDROID_LOG_INFO, "Gecko",
1098 "Blacklisted device `%s'", *hw);
1099 broken = true;
1100 break;
1101 }
1102 }
1103 break;
1104 }
1105 std::getline(cpuinfo, line);
1106 } while(!cpuinfo.fail() && !cpuinfo.eof());
1107
1108 __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken",
1109 broken ? "" : "not ");
1110
1111 return broken;
1112 #endif // ifndef ANDROID
1113 }
1114
1115 static bool
IsJITBrokenHere()1116 IsJITBrokenHere()
1117 {
1118 static bool computedIsBroken = false;
1119 static bool isBroken = false;
1120 if (!computedIsBroken) {
1121 isBroken = ComputeIsJITBroken();
1122 computedIsBroken = true;
1123 }
1124 return isBroken;
1125 }
1126
1127 void
updateJITEnabled()1128 JSContext::updateJITEnabled()
1129 {
1130 jitIsBroken = IsJITBrokenHere();
1131 }
1132
1133 size_t
sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const1134 JSContext::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1135 {
1136 /*
1137 * There are other JSContext members that could be measured; the following
1138 * ones have been found by DMD to be worth measuring. More stuff may be
1139 * added later.
1140 */
1141 return mallocSizeOf(this) + cycleDetectorSet.sizeOfExcludingThis(mallocSizeOf);
1142 }
1143
1144 void
mark(JSTracer * trc)1145 JSContext::mark(JSTracer* trc)
1146 {
1147 /* Stack frames and slots are traced by StackSpace::mark. */
1148
1149 TraceCycleDetectionSet(trc, cycleDetectorSet);
1150
1151 if (compartment_)
1152 compartment_->mark();
1153 }
1154
1155 void*
stackLimitAddressForJitCode(StackKind kind)1156 ExclusiveContext::stackLimitAddressForJitCode(StackKind kind)
1157 {
1158 #ifdef JS_SIMULATOR
1159 return runtime_->addressOfSimulatorStackLimit();
1160 #else
1161 return stackLimitAddress(kind);
1162 #endif
1163 }
1164
1165 JSVersion
findVersion() const1166 JSContext::findVersion() const
1167 {
1168 if (JSScript* script = currentScript(nullptr, ALLOW_CROSS_COMPARTMENT))
1169 return script->getVersion();
1170
1171 if (compartment() && compartment()->options().version() != JSVERSION_UNKNOWN)
1172 return compartment()->options().version();
1173
1174 return runtime()->defaultVersion();
1175 }
1176
1177 #ifdef DEBUG
1178
AutoCheckRequestDepth(JSContext * cx)1179 JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext* cx)
1180 : cx(cx)
1181 {
1182 MOZ_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
1183 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1184 cx->runtime()->checkRequestDepth++;
1185 }
1186
AutoCheckRequestDepth(ContextFriendFields * cxArg)1187 JS::AutoCheckRequestDepth::AutoCheckRequestDepth(ContextFriendFields* cxArg)
1188 : cx(static_cast<ExclusiveContext*>(cxArg)->maybeJSContext())
1189 {
1190 if (cx) {
1191 MOZ_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
1192 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1193 cx->runtime()->checkRequestDepth++;
1194 }
1195 }
1196
~AutoCheckRequestDepth()1197 JS::AutoCheckRequestDepth::~AutoCheckRequestDepth()
1198 {
1199 if (cx) {
1200 MOZ_ASSERT(cx->runtime()->checkRequestDepth != 0);
1201 cx->runtime()->checkRequestDepth--;
1202 }
1203 }
1204
1205 #endif
1206
1207 #ifdef JS_CRASH_DIAGNOSTICS
1208 void
check(InterpreterFrame * fp)1209 CompartmentChecker::check(InterpreterFrame* fp)
1210 {
1211 if (fp)
1212 check(fp->scopeChain());
1213 }
1214
1215 void
check(AbstractFramePtr frame)1216 CompartmentChecker::check(AbstractFramePtr frame)
1217 {
1218 if (frame)
1219 check(frame.scopeChain());
1220 }
1221 #endif
1222
1223 void
crash(const char * reason)1224 AutoEnterOOMUnsafeRegion::crash(const char* reason)
1225 {
1226 char msgbuf[1024];
1227 JS_snprintf(msgbuf, sizeof(msgbuf), "[unhandlable oom] %s", reason);
1228 MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
1229 MOZ_CRASH();
1230 }
1231