Line data Source code
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 "vm/JSContext-inl.h"
12 :
13 : #include "mozilla/ArrayUtils.h"
14 : #include "mozilla/DebugOnly.h"
15 : #include "mozilla/MemoryReporting.h"
16 : #include "mozilla/Sprintf.h"
17 : #include "mozilla/Unused.h"
18 :
19 : #include <ctype.h>
20 : #include <stdarg.h>
21 : #include <string.h>
22 : #ifdef ANDROID
23 : # include <android/log.h>
24 : # include <fstream>
25 : # include <string>
26 : #endif // ANDROID
27 : #ifdef XP_WIN
28 : # include <processthreadsapi.h>
29 : #endif // XP_WIN
30 :
31 : #include "jsexn.h"
32 : #include "jspubtd.h"
33 : #include "jstypes.h"
34 :
35 : #include "builtin/String.h"
36 : #include "gc/FreeOp.h"
37 : #include "gc/Marking.h"
38 : #include "jit/Ion.h"
39 : #include "jit/PcScriptCache.h"
40 : #include "js/CharacterEncoding.h"
41 : #include "js/Printf.h"
42 : #include "util/DoubleToString.h"
43 : #include "util/NativeStack.h"
44 : #include "util/Windows.h"
45 : #include "vm/BytecodeUtil.h"
46 : #include "vm/ErrorReporting.h"
47 : #include "vm/HelperThreads.h"
48 : #include "vm/Iteration.h"
49 : #include "vm/JSAtom.h"
50 : #include "vm/JSFunction.h"
51 : #include "vm/JSObject.h"
52 : #include "vm/JSScript.h"
53 : #include "vm/Realm.h"
54 : #include "vm/Shape.h"
55 :
56 : #include "vm/Compartment-inl.h"
57 : #include "vm/JSObject-inl.h"
58 : #include "vm/JSScript-inl.h"
59 : #include "vm/Stack-inl.h"
60 :
61 : using namespace js;
62 : using namespace js::gc;
63 :
64 : using mozilla::DebugOnly;
65 : using mozilla::PodArrayZero;
66 :
67 : bool
68 1283 : js::AutoCycleDetector::init()
69 : {
70 1283 : MOZ_ASSERT(cyclic);
71 :
72 2566 : AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
73 :
74 0 : for (JSObject* obj2 : vector) {
75 0 : if (MOZ_UNLIKELY(obj == obj2))
76 : return true;
77 : }
78 :
79 2566 : if (!vector.append(obj))
80 : return false;
81 :
82 0 : cyclic = false;
83 1283 : return true;
84 : }
85 :
86 2566 : js::AutoCycleDetector::~AutoCycleDetector()
87 : {
88 0 : if (MOZ_LIKELY(!cyclic)) {
89 0 : AutoCycleDetector::Vector& vec = cx->cycleDetectorVector();
90 0 : MOZ_ASSERT(vec.back() == obj);
91 1283 : if (vec.length() > 1) {
92 : vec.popBack();
93 : } else {
94 : // Avoid holding on to unused heap allocations.
95 : vec.clearAndFree();
96 : }
97 : }
98 1283 : }
99 :
100 : bool
101 8 : JSContext::init(ContextKind kind)
102 : {
103 : // Skip most of the initialization if this thread will not be running JS.
104 0 : if (kind == ContextKind::MainThread) {
105 4 : if (!regexpStack.ref().init())
106 : return false;
107 :
108 4 : if (!fx.initInstance())
109 : return false;
110 :
111 : #ifdef JS_SIMULATOR
112 : simulator_ = jit::Simulator::Create(this);
113 : if (!simulator_)
114 : return false;
115 : #endif
116 :
117 4 : if (!wasm::EnsureSignalHandlers(this))
118 : return false;
119 : }
120 :
121 : // Set the ContextKind last, so that ProtectedData checks will allow us to
122 : // initialize this context before it becomes the runtime's active context.
123 8 : kind_ = kind;
124 :
125 : return true;
126 : }
127 :
128 : JSContext*
129 4 : js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
130 : {
131 4 : AutoNoteSingleThreadedRegion anstr;
132 :
133 4 : MOZ_RELEASE_ASSERT(!TlsContext.get());
134 :
135 : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
136 0 : js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
137 4 : : js::THREAD_TYPE_WORKER);
138 : #endif
139 :
140 0 : JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
141 4 : if (!runtime)
142 : return nullptr;
143 :
144 0 : JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
145 0 : if (!cx) {
146 0 : js_delete(runtime);
147 : return nullptr;
148 : }
149 :
150 0 : if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
151 0 : runtime->destroyRuntime();
152 0 : js_delete(cx);
153 0 : js_delete(runtime);
154 : return nullptr;
155 : }
156 :
157 0 : if (!cx->init(ContextKind::MainThread)) {
158 0 : runtime->destroyRuntime();
159 0 : js_delete(cx);
160 0 : js_delete(runtime);
161 : return nullptr;
162 : }
163 :
164 : return cx;
165 : }
166 :
167 : static void
168 0 : FreeJobQueueHandling(JSContext* cx)
169 : {
170 0 : if (!cx->jobQueue)
171 : return;
172 :
173 0 : cx->jobQueue->reset();
174 0 : FreeOp* fop = cx->defaultFreeOp();
175 0 : fop->delete_(cx->jobQueue.ref());
176 0 : cx->getIncumbentGlobalCallback = nullptr;
177 0 : cx->enqueuePromiseJobCallback = nullptr;
178 0 : cx->enqueuePromiseJobCallbackData = nullptr;
179 : }
180 :
181 : void
182 0 : js::DestroyContext(JSContext* cx)
183 : {
184 0 : JS_AbortIfWrongThread(cx);
185 :
186 0 : if (cx->outstandingRequests != 0)
187 0 : MOZ_CRASH("Attempted to destroy a context while it is in a request.");
188 :
189 0 : cx->checkNoGCRooters();
190 :
191 : // Cancel all off thread Ion compiles. Completed Ion compiles may try to
192 : // interrupt this context. See HelperThread::handleIonWorkload.
193 0 : CancelOffThreadIonCompile(cx->runtime());
194 :
195 0 : FreeJobQueueHandling(cx);
196 :
197 : // Flush promise tasks executing in helper threads early, before any parts
198 : // of the JSRuntime that might be visible to helper threads are torn down.
199 0 : cx->runtime()->offThreadPromiseState.ref().shutdown(cx);
200 :
201 : // Destroy the runtime along with its last context.
202 0 : cx->runtime()->destroyRuntime();
203 0 : js_delete(cx->runtime());
204 0 : js_delete_poison(cx);
205 0 : }
206 :
207 : void
208 0 : JS::RootingContext::checkNoGCRooters() {
209 : #ifdef DEBUG
210 0 : for (auto const& stackRootPtr : stackRoots_)
211 0 : MOZ_ASSERT(stackRootPtr == nullptr);
212 : #endif
213 0 : }
214 :
215 : bool
216 152 : AutoResolving::alreadyStartedSlow() const
217 : {
218 152 : MOZ_ASSERT(link);
219 : AutoResolving* cursor = link;
220 0 : do {
221 0 : MOZ_ASSERT(this != cursor);
222 456 : if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
223 : return true;
224 152 : } while (!!(cursor = cursor->link));
225 : return false;
226 : }
227 :
228 : static void
229 22 : ReportError(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback,
230 : void* userRef)
231 : {
232 : /*
233 : * Check the error report, and set a JavaScript-catchable exception
234 : * if the error is defined to have an associated exception. If an
235 : * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
236 : * on the error report, and exception-aware hosts should ignore it.
237 : */
238 0 : MOZ_ASSERT(reportp);
239 0 : if ((!callback || callback == GetErrorMessage) &&
240 10 : reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
241 : {
242 0 : reportp->flags |= JSREPORT_EXCEPTION;
243 : }
244 :
245 0 : if (JSREPORT_IS_WARNING(reportp->flags)) {
246 0 : CallWarningReporter(cx, reportp);
247 0 : return;
248 : }
249 :
250 22 : ErrorToException(cx, reportp, callback, userRef);
251 : }
252 :
253 : /*
254 : * The given JSErrorReport object have been zeroed and must not outlive
255 : * cx->fp() (otherwise owned fields may become invalid).
256 : */
257 : static void
258 22 : PopulateReportBlame(JSContext* cx, JSErrorReport* report)
259 : {
260 0 : JS::Realm* realm = cx->realm();
261 0 : if (!realm)
262 0 : return;
263 :
264 : /*
265 : * Walk stack until we find a frame that is associated with a non-builtin
266 : * rather than a builtin frame and which we're allowed to know about.
267 : */
268 0 : NonBuiltinFrameIter iter(cx, realm->principals());
269 22 : if (iter.done())
270 : return;
271 :
272 22 : report->filename = iter.filename();
273 : uint32_t column;
274 0 : report->lineno = iter.computeLine(&column);
275 0 : report->column = FixupColumnForDisplay(column);
276 22 : report->isMuted = iter.mutedErrors();
277 : }
278 :
279 : /*
280 : * Since memory has been exhausted, avoid the normal error-handling path which
281 : * allocates an error object, report and callstack. If code is running, simply
282 : * throw the static atom "out of memory". If code is not running, call the
283 : * error reporter directly.
284 : *
285 : * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
286 : * not occur, so GC must be avoided or suppressed.
287 : */
288 : JS_FRIEND_API(void)
289 0 : js::ReportOutOfMemory(JSContext* cx)
290 : {
291 : #ifdef JS_MORE_DETERMINISTIC
292 : /*
293 : * OOMs are non-deterministic, especially across different execution modes
294 : * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
295 : * so that the fuzzers can detect this.
296 : */
297 : fprintf(stderr, "ReportOutOfMemory called\n");
298 : #endif
299 :
300 0 : if (cx->helperThread())
301 0 : return cx->addPendingOutOfMemory();
302 :
303 0 : cx->runtime()->hadOutOfMemory = true;
304 0 : AutoSuppressGC suppressGC(cx);
305 :
306 : /* Report the oom. */
307 0 : if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback)
308 0 : oomCallback(cx, cx->runtime()->oomCallbackData);
309 :
310 0 : cx->setPendingException(StringValue(cx->names().outOfMemory));
311 : }
312 :
313 : mozilla::GenericErrorResult<OOM&>
314 0 : js::ReportOutOfMemoryResult(JSContext* cx)
315 : {
316 0 : ReportOutOfMemory(cx);
317 0 : return cx->alreadyReportedOOM();
318 : }
319 :
320 : void
321 0 : js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber)
322 : {
323 : #ifdef JS_MORE_DETERMINISTIC
324 : /*
325 : * We cannot make stack depth deterministic across different
326 : * implementations (e.g. JIT vs. interpreter will differ in
327 : * their maximum stack depth).
328 : * However, we can detect externally when we hit the maximum
329 : * stack depth which is useful for external testing programs
330 : * like fuzzers.
331 : */
332 : fprintf(stderr, "ReportOverRecursed called\n");
333 : #endif
334 0 : if (maybecx) {
335 0 : if (!maybecx->helperThread()) {
336 0 : JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr, errorNumber);
337 0 : maybecx->overRecursed_ = true;
338 : } else {
339 0 : maybecx->addPendingOverRecursed();
340 : }
341 : }
342 0 : }
343 :
344 : JS_FRIEND_API(void)
345 0 : js::ReportOverRecursed(JSContext* maybecx)
346 : {
347 0 : ReportOverRecursed(maybecx, JSMSG_OVER_RECURSED);
348 0 : }
349 :
350 : void
351 0 : js::ReportAllocationOverflow(JSContext* cx)
352 : {
353 0 : if (!cx)
354 0 : return;
355 :
356 0 : if (cx->helperThread())
357 : return;
358 :
359 0 : AutoSuppressGC suppressGC(cx);
360 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
361 : }
362 :
363 : /*
364 : * Given flags and the state of cx, decide whether we should report an
365 : * error, a warning, or just continue execution normally. Return
366 : * true if we should continue normally, without reporting anything;
367 : * otherwise, adjust *flags as appropriate and return false.
368 : */
369 : static bool
370 22 : checkReportFlags(JSContext* cx, unsigned* flags)
371 : {
372 22 : if (JSREPORT_IS_STRICT(*flags)) {
373 : /* Warning/error only when JSOPTION_STRICT is set. */
374 0 : if (!cx->realm()->behaviors().extraWarnings(cx))
375 : return true;
376 : }
377 :
378 : /* Warnings become errors when JSOPTION_WERROR is set. */
379 0 : if (JSREPORT_IS_WARNING(*flags) && cx->options().werror())
380 0 : *flags &= ~JSREPORT_WARNING;
381 :
382 : return false;
383 : }
384 :
385 : bool
386 8 : js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format,
387 : ErrorArgumentsType argumentsType, va_list ap)
388 : {
389 16 : JSErrorReport report;
390 :
391 8 : if (checkReportFlags(cx, &flags))
392 : return true;
393 :
394 0 : UniqueChars message(JS_vsmprintf(format, ap));
395 0 : if (!message) {
396 0 : ReportOutOfMemory(cx);
397 0 : return false;
398 : }
399 :
400 10 : MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(message.get()));
401 :
402 0 : report.flags = flags;
403 0 : report.errorNumber = JSMSG_USER_DEFINED_ERROR;
404 0 : if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreUTF8) {
405 8 : report.initOwnedMessage(message.release());
406 : } else {
407 0 : MOZ_ASSERT(argumentsType == ArgumentsAreLatin1);
408 0 : Latin1Chars latin1(message.get(), strlen(message.get()));
409 0 : UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1));
410 0 : if (!utf8)
411 0 : return false;
412 0 : report.initOwnedMessage(reinterpret_cast<const char*>(utf8.get()));
413 : }
414 8 : PopulateReportBlame(cx, &report);
415 :
416 8 : bool warning = JSREPORT_IS_WARNING(report.flags);
417 :
418 0 : ReportError(cx, &report, nullptr, nullptr);
419 8 : return warning;
420 : }
421 :
422 : /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
423 : void
424 0 : js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg)
425 : {
426 0 : RootedValue usage(cx);
427 0 : if (!JS_GetProperty(cx, callee, "usage", &usage))
428 0 : return;
429 :
430 0 : if (!usage.isString()) {
431 0 : JS_ReportErrorASCII(cx, "%s", msg);
432 : } else {
433 0 : RootedString usageStr(cx, usage.toString());
434 0 : JSAutoByteString str;
435 0 : if (!str.encodeUtf8(cx, usageStr))
436 0 : return;
437 0 : JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.ptr());
438 : }
439 : }
440 :
441 : enum class PrintErrorKind {
442 : Error,
443 : Warning,
444 : StrictWarning,
445 : Note
446 : };
447 :
448 : static void
449 0 : PrintErrorLine(FILE* file, const char* prefix, JSErrorReport* report)
450 : {
451 0 : if (const char16_t* linebuf = report->linebuf()) {
452 0 : size_t n = report->linebufLength();
453 :
454 0 : fputs(":\n", file);
455 0 : if (prefix)
456 0 : fputs(prefix, file);
457 :
458 0 : for (size_t i = 0; i < n; i++)
459 0 : fputc(static_cast<char>(linebuf[i]), file);
460 :
461 : // linebuf usually ends with a newline. If not, add one here.
462 0 : if (n == 0 || linebuf[n-1] != '\n')
463 0 : fputc('\n', file);
464 :
465 0 : if (prefix)
466 0 : fputs(prefix, file);
467 :
468 0 : n = report->tokenOffset();
469 0 : for (size_t i = 0, j = 0; i < n; i++) {
470 0 : if (linebuf[i] == '\t') {
471 0 : for (size_t k = (j + 8) & ~7; j < k; j++)
472 0 : fputc('.', file);
473 : continue;
474 : }
475 0 : fputc('.', file);
476 0 : j++;
477 : }
478 0 : fputc('^', file);
479 : }
480 0 : }
481 :
482 : static void
483 : PrintErrorLine(FILE* file, const char* prefix, JSErrorNotes::Note* note)
484 : {
485 : }
486 :
487 : template <typename T>
488 : static bool
489 0 : PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
490 : T* report, PrintErrorKind kind)
491 : {
492 0 : UniqueChars prefix;
493 0 : if (report->filename)
494 0 : prefix = JS_smprintf("%s:", report->filename);
495 :
496 0 : if (report->lineno) {
497 0 : prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
498 : report->column);
499 : }
500 :
501 0 : if (kind != PrintErrorKind::Error) {
502 0 : const char* kindPrefix = nullptr;
503 0 : switch (kind) {
504 : case PrintErrorKind::Error:
505 0 : MOZ_CRASH("unreachable");
506 : case PrintErrorKind::Warning:
507 0 : kindPrefix = "warning";
508 : break;
509 : case PrintErrorKind::StrictWarning:
510 0 : kindPrefix = "strict warning";
511 : break;
512 : case PrintErrorKind::Note:
513 0 : kindPrefix = "note";
514 : break;
515 : }
516 :
517 0 : prefix = JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix);
518 : }
519 :
520 0 : const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
521 :
522 : /* embedded newlines -- argh! */
523 : const char* ctmp;
524 0 : while ((ctmp = strchr(message, '\n')) != 0) {
525 0 : ctmp++;
526 0 : if (prefix)
527 0 : fputs(prefix.get(), file);
528 0 : mozilla::Unused << fwrite(message, 1, ctmp - message, file);
529 0 : message = ctmp;
530 : }
531 :
532 : /* If there were no filename or lineno, the prefix might be empty */
533 0 : if (prefix)
534 0 : fputs(prefix.get(), file);
535 0 : fputs(message, file);
536 :
537 0 : PrintErrorLine(file, prefix.get(), report);
538 0 : fputc('\n', file);
539 :
540 0 : fflush(file);
541 0 : return true;
542 : }
543 :
544 : bool
545 0 : js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
546 : JSErrorReport* report, bool reportWarnings)
547 : {
548 0 : MOZ_ASSERT(report);
549 :
550 : /* Conditionally ignore reported warnings. */
551 0 : if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
552 : return false;
553 :
554 0 : PrintErrorKind kind = PrintErrorKind::Error;
555 0 : if (JSREPORT_IS_WARNING(report->flags)) {
556 0 : if (JSREPORT_IS_STRICT(report->flags))
557 : kind = PrintErrorKind::StrictWarning;
558 : else
559 0 : kind = PrintErrorKind::Warning;
560 : }
561 0 : PrintSingleError(cx, file, toStringResult, report, kind);
562 :
563 0 : if (report->notes) {
564 0 : for (auto&& note : *report->notes)
565 0 : PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(), PrintErrorKind::Note);
566 : }
567 :
568 : return true;
569 : }
570 :
571 : class MOZ_RAII AutoMessageArgs
572 : {
573 : size_t totalLength_;
574 : /* only {0} thru {9} supported */
575 : mozilla::Array<const char*, JS::MaxNumErrorArguments> args_;
576 : mozilla::Array<size_t, JS::MaxNumErrorArguments> lengths_;
577 : uint16_t count_;
578 : bool allocatedElements_ : 1;
579 :
580 : public:
581 : AutoMessageArgs()
582 42 : : totalLength_(0), count_(0), allocatedElements_(false)
583 : {
584 28 : PodArrayZero(args_);
585 : }
586 :
587 0 : ~AutoMessageArgs()
588 14 : {
589 : /* free the arguments only if we allocated them */
590 14 : if (allocatedElements_) {
591 : uint16_t i = 0;
592 0 : while (i < count_) {
593 0 : if (args_[i])
594 0 : js_free((void*)args_[i]);
595 16 : i++;
596 : }
597 : }
598 14 : }
599 :
600 0 : const char* args(size_t i) const {
601 0 : MOZ_ASSERT(i < count_);
602 32 : return args_[i];
603 : }
604 :
605 : size_t totalLength() const {
606 : return totalLength_;
607 : }
608 :
609 0 : size_t lengths(size_t i) const {
610 0 : MOZ_ASSERT(i < count_);
611 64 : return lengths_[i];
612 : }
613 :
614 : uint16_t count() const {
615 : return count_;
616 : }
617 :
618 : /* Gather the arguments into an array, and accumulate their sizes. */
619 14 : bool init(JSContext* cx, const char16_t** argsArg, uint16_t countArg,
620 : ErrorArgumentsType typeArg, va_list ap) {
621 14 : MOZ_ASSERT(countArg > 0);
622 :
623 14 : count_ = countArg;
624 :
625 0 : for (uint16_t i = 0; i < count_; i++) {
626 16 : switch (typeArg) {
627 : case ArgumentsAreASCII:
628 : case ArgumentsAreUTF8: {
629 0 : MOZ_ASSERT(!argsArg);
630 0 : args_[i] = va_arg(ap, char*);
631 0 : MOZ_ASSERT_IF(typeArg == ArgumentsAreASCII, JS::StringIsASCII(args_[i]));
632 0 : lengths_[i] = strlen(args_[i]);
633 0 : break;
634 : }
635 : case ArgumentsAreLatin1: {
636 0 : MOZ_ASSERT(!argsArg);
637 0 : const Latin1Char* latin1 = va_arg(ap, Latin1Char*);
638 0 : size_t len = strlen(reinterpret_cast<const char*>(latin1));
639 0 : mozilla::Range<const Latin1Char> range(latin1, len);
640 0 : char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
641 0 : if (!utf8)
642 0 : return false;
643 :
644 0 : args_[i] = utf8;
645 0 : lengths_[i] = strlen(utf8);
646 0 : allocatedElements_ = true;
647 4 : break;
648 : }
649 : case ArgumentsAreUnicode: {
650 0 : const char16_t* uc = argsArg ? argsArg[i] : va_arg(ap, char16_t*);
651 0 : size_t len = js_strlen(uc);
652 0 : mozilla::Range<const char16_t> range(uc, len);
653 0 : char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
654 1 : if (!utf8)
655 0 : return false;
656 :
657 0 : args_[i] = utf8;
658 0 : lengths_[i] = strlen(utf8);
659 0 : allocatedElements_ = true;
660 12 : break;
661 : }
662 : }
663 32 : totalLength_ += lengths_[i];
664 : }
665 : return true;
666 : }
667 : };
668 :
669 : static void
670 : SetExnType(JSErrorReport* reportp, int16_t exnType)
671 : {
672 14 : reportp->exnType = exnType;
673 : }
674 :
675 : static void
676 : SetExnType(JSErrorNotes::Note* notep, int16_t exnType)
677 : {
678 : // Do nothing for JSErrorNotes::Note.
679 : }
680 :
681 : /*
682 : * The arguments from ap need to be packaged up into an array and stored
683 : * into the report struct.
684 : *
685 : * The format string addressed by the error number may contain operands
686 : * identified by the format {N}, where N is a decimal digit. Each of these
687 : * is to be replaced by the Nth argument from the va_list. The complete
688 : * message is placed into reportp->message_.
689 : *
690 : * Returns true if the expansion succeeds (can fail if out of memory).
691 : */
692 : template <typename T>
693 : bool
694 14 : ExpandErrorArgumentsHelper(JSContext* cx, JSErrorCallback callback,
695 : void* userRef, const unsigned errorNumber,
696 : const char16_t** messageArgs,
697 : ErrorArgumentsType argumentsType,
698 : T* reportp, va_list ap)
699 : {
700 : const JSErrorFormatString* efs;
701 :
702 1 : if (!callback)
703 0 : callback = GetErrorMessage;
704 :
705 : {
706 0 : AutoSuppressGC suppressGC(cx);
707 14 : efs = callback(userRef, errorNumber);
708 : }
709 :
710 0 : if (efs) {
711 28 : SetExnType(reportp, efs->exnType);
712 :
713 14 : MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(efs->format));
714 :
715 0 : uint16_t argCount = efs->argCount;
716 0 : MOZ_RELEASE_ASSERT(argCount <= JS::MaxNumErrorArguments);
717 14 : if (argCount > 0) {
718 : /*
719 : * Parse the error format, substituting the argument X
720 : * for {X} in the format.
721 : */
722 14 : if (efs->format) {
723 : const char* fmt;
724 : char* out;
725 : #ifdef DEBUG
726 14 : int expandedArgs = 0;
727 : #endif
728 : size_t expandedLength;
729 14 : size_t len = strlen(efs->format);
730 :
731 0 : AutoMessageArgs args;
732 0 : if (!args.init(cx, messageArgs, argCount, argumentsType, ap))
733 0 : return false;
734 :
735 0 : expandedLength = len
736 0 : - (3 * args.count()) /* exclude the {n} */
737 14 : + args.totalLength();
738 :
739 : /*
740 : * Note - the above calculation assumes that each argument
741 : * is used once and only once in the expansion !!!
742 : */
743 0 : char* utf8 = out = cx->pod_malloc<char>(expandedLength + 1);
744 14 : if (!out)
745 : return false;
746 :
747 0 : fmt = efs->format;
748 0 : while (*fmt) {
749 0 : if (*fmt == '{') {
750 0 : if (isdigit(fmt[1])) {
751 0 : int d = JS7_UNDEC(fmt[1]);
752 0 : MOZ_RELEASE_ASSERT(d < args.count());
753 0 : strncpy(out, args.args(d), args.lengths(d));
754 0 : out += args.lengths(d);
755 16 : fmt += 3;
756 : #ifdef DEBUG
757 16 : expandedArgs++;
758 : #endif
759 16 : continue;
760 : }
761 : }
762 248 : *out++ = *fmt++;
763 : }
764 0 : MOZ_ASSERT(expandedArgs == args.count());
765 14 : *out = 0;
766 :
767 28 : reportp->initOwnedMessage(utf8);
768 : }
769 : } else {
770 : /* Non-null messageArgs should have at least one non-null arg. */
771 0 : MOZ_ASSERT(!messageArgs);
772 : /*
773 : * Zero arguments: the format string (if it exists) is the
774 : * entire message.
775 : */
776 0 : if (efs->format)
777 0 : reportp->initBorrowedMessage(efs->format);
778 : }
779 : }
780 14 : if (!reportp->message()) {
781 : /* where's the right place for this ??? */
782 : const char* defaultErrorMessage
783 0 : = "No error message available for error number %d";
784 0 : size_t nbytes = strlen(defaultErrorMessage) + 16;
785 0 : char* message = cx->pod_malloc<char>(nbytes);
786 0 : if (!message)
787 : return false;
788 0 : snprintf(message, nbytes, defaultErrorMessage, errorNumber);
789 0 : reportp->initOwnedMessage(message);
790 : }
791 : return true;
792 : }
793 :
794 : bool
795 0 : js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
796 : void* userRef, const unsigned errorNumber,
797 : const char16_t** messageArgs,
798 : ErrorArgumentsType argumentsType,
799 : JSErrorReport* reportp, va_list ap)
800 : {
801 : return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
802 14 : messageArgs, argumentsType, reportp, ap);
803 : }
804 :
805 : bool
806 0 : js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
807 : void* userRef, const unsigned errorNumber,
808 : const char16_t** messageArgs,
809 : ErrorArgumentsType argumentsType,
810 : JSErrorNotes::Note* notep, va_list ap)
811 : {
812 : return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
813 0 : messageArgs, argumentsType, notep, ap);
814 : }
815 :
816 : bool
817 2 : js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
818 : void* userRef, const unsigned errorNumber,
819 : ErrorArgumentsType argumentsType, va_list ap)
820 : {
821 4 : JSErrorReport report;
822 : bool warning;
823 :
824 2 : if (checkReportFlags(cx, &flags))
825 : return true;
826 2 : warning = JSREPORT_IS_WARNING(flags);
827 :
828 0 : report.flags = flags;
829 0 : report.errorNumber = errorNumber;
830 2 : PopulateReportBlame(cx, &report);
831 :
832 2 : if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
833 : nullptr, argumentsType, &report, ap)) {
834 : return false;
835 : }
836 :
837 2 : ReportError(cx, &report, callback, userRef);
838 :
839 2 : return warning;
840 : }
841 :
842 : static bool
843 12 : ExpandErrorArguments(JSContext* cx, JSErrorCallback callback,
844 : void* userRef, const unsigned errorNumber,
845 : const char16_t** messageArgs,
846 : ErrorArgumentsType argumentsType,
847 : JSErrorReport* reportp, ...)
848 : {
849 : va_list ap;
850 12 : va_start(ap, reportp);
851 : bool expanded = js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
852 0 : messageArgs, argumentsType, reportp, ap);
853 0 : va_end(ap);
854 12 : return expanded;
855 : }
856 :
857 : bool
858 12 : js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
859 : void* userRef, const unsigned errorNumber,
860 : const char16_t** args)
861 : {
862 12 : if (checkReportFlags(cx, &flags))
863 : return true;
864 12 : bool warning = JSREPORT_IS_WARNING(flags);
865 :
866 0 : JSErrorReport report;
867 0 : report.flags = flags;
868 0 : report.errorNumber = errorNumber;
869 12 : PopulateReportBlame(cx, &report);
870 :
871 12 : if (!ExpandErrorArguments(cx, callback, userRef, errorNumber,
872 : args, ArgumentsAreUnicode, &report))
873 : {
874 : return false;
875 : }
876 :
877 12 : ReportError(cx, &report, callback, userRef);
878 :
879 12 : return warning;
880 : }
881 :
882 : void
883 0 : js::ReportIsNotDefined(JSContext* cx, HandleId id)
884 : {
885 0 : JSAutoByteString printable;
886 0 : if (!ValueToPrintableUTF8(cx, IdToValue(id), &printable))
887 0 : return;
888 0 : JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.ptr());
889 : }
890 :
891 : void
892 0 : js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
893 : {
894 0 : RootedId id(cx, NameToId(name));
895 0 : ReportIsNotDefined(cx, id);
896 0 : }
897 :
898 : void
899 2 : js::ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v)
900 : {
901 : MOZ_ASSERT(v.isNullOrUndefined());
902 :
903 : UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, nullptr);
904 0 : if (!bytes)
905 2 : return;
906 :
907 : if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
908 0 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
909 0 : bytes.get());
910 0 : } else if (v.isUndefined()) {
911 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
912 : bytes.get(), js_undefined_str);
913 0 : } else {
914 0 : MOZ_ASSERT(v.isNull());
915 1 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
916 : bytes.get(), js_null_str);
917 : }
918 1 : }
919 :
920 0 : void
921 1 : js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
922 : {
923 : char argbuf[11];
924 1 : UniqueChars bytes;
925 :
926 : SprintfLiteral(argbuf, "%u", arg);
927 : if (IsFunctionObject(v)) {
928 : RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
929 : bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
930 : if (!bytes)
931 0 : return;
932 : }
933 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
934 0 : JSMSG_MISSING_FUN_ARG,
935 : argbuf, bytes ? bytes.get() : "");
936 0 : }
937 0 :
938 0 : bool
939 0 : js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
940 0 : int spindex, HandleValue v, HandleString fallback,
941 0 : const char* arg1, const char* arg2)
942 : {
943 0 : MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
944 : MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
945 0 : UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
946 : if (!bytes)
947 : return false;
948 :
949 0 : return JS_ReportErrorFlagsAndNumberLatin1(cx, flags, GetErrorMessage, nullptr, errorNumber,
950 : bytes.get(), arg1, arg2);
951 : }
952 :
953 0 : JSObject*
954 : js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
955 : {
956 0 : RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
957 0 : if (!notesArray)
958 0 : return nullptr;
959 0 :
960 : if (!report->notes)
961 : return notesArray;
962 0 :
963 0 : for (auto&& note : *report->notes) {
964 0 : RootedPlainObject noteObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
965 : if (!noteObj)
966 : return nullptr;
967 :
968 0 : RootedString messageStr(cx, note->newMessageString(cx));
969 : if (!messageStr)
970 0 : return nullptr;
971 0 : RootedValue messageVal(cx, StringValue(messageStr));
972 : if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal))
973 : return nullptr;
974 0 :
975 : RootedValue filenameVal(cx);
976 : if (note->filename) {
977 0 : RootedString filenameStr(cx, NewStringCopyZ<CanGC>(cx, note->filename));
978 0 : if (!filenameStr)
979 0 : return nullptr;
980 0 : filenameVal = StringValue(filenameStr);
981 : }
982 0 : if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal))
983 0 : return nullptr;
984 0 :
985 0 : RootedValue linenoVal(cx, Int32Value(note->lineno));
986 0 : if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal))
987 0 : return nullptr;
988 : RootedValue columnVal(cx, Int32Value(note->column));
989 0 : if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal))
990 0 : return nullptr;
991 0 :
992 0 : if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj)))
993 0 : return nullptr;
994 0 : }
995 :
996 0 : return notesArray;
997 : }
998 :
999 0 : const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1000 0 : #define MSG_DEF(name, count, exception, format) \
1001 0 : { #name, format, count, exception } ,
1002 0 : #include "js.msg"
1003 0 : #undef MSG_DEF
1004 0 : };
1005 :
1006 0 : JS_FRIEND_API(const JSErrorFormatString*)
1007 : js::GetErrorMessage(void* userRef, const unsigned errorNumber)
1008 : {
1009 : if (errorNumber > 0 && errorNumber < JSErr_Limit)
1010 0 : return &js_ErrorFormatString[errorNumber];
1011 : return nullptr;
1012 : }
1013 :
1014 : void
1015 : JSContext::recoverFromOutOfMemory()
1016 : {
1017 : if (helperThread()) {
1018 : // Keep in sync with addPendingOutOfMemory.
1019 : if (ParseTask* task = helperThread()->parseTask())
1020 : task->outOfMemory = false;
1021 14 : } else {
1022 : if (isExceptionPending()) {
1023 0 : MOZ_ASSERT(isThrowingOutOfMemory());
1024 14 : clearPendingException();
1025 : }
1026 : }
1027 : }
1028 :
1029 0 : static bool
1030 : InternalEnqueuePromiseJobCallback(JSContext* cx, JS::HandleObject job,
1031 0 : JS::HandleObject allocationSite,
1032 : JS::HandleObject incumbentGlobal, void* data)
1033 0 : {
1034 0 : MOZ_ASSERT(job);
1035 : return cx->jobQueue->append(job);
1036 0 : }
1037 0 :
1038 0 : namespace {
1039 : class MOZ_STACK_CLASS ReportExceptionClosure : public ScriptEnvironmentPreparer::Closure
1040 : {
1041 0 : public:
1042 : explicit ReportExceptionClosure(HandleValue exn)
1043 : : exn_(exn)
1044 0 : {
1045 : }
1046 :
1047 : bool operator()(JSContext* cx) override
1048 0 : {
1049 0 : cx->setPendingException(exn_);
1050 : return false;
1051 : }
1052 :
1053 : private:
1054 : HandleValue exn_;
1055 : };
1056 : } // anonymous namespace
1057 0 :
1058 : JS_FRIEND_API(bool)
1059 : js::UseInternalJobQueues(JSContext* cx)
1060 : {
1061 0 : // Internal job queue handling must be set up very early. Self-hosting
1062 : // initialization is as good a marker for that as any.
1063 0 : MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
1064 0 : "js::UseInternalJobQueues must be called early during runtime startup.");
1065 : MOZ_ASSERT(!cx->jobQueue);
1066 : auto* queue = js_new<PersistentRooted<JobQueue>>(cx, JobQueue(SystemAllocPolicy()));
1067 : if (!queue)
1068 : return false;
1069 :
1070 : cx->jobQueue = queue;
1071 :
1072 : cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue();
1073 0 : MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
1074 :
1075 : JS::SetEnqueuePromiseJobCallback(cx, InternalEnqueuePromiseJobCallback);
1076 :
1077 0 : return true;
1078 : }
1079 0 :
1080 0 : JS_FRIEND_API(bool)
1081 0 : js::EnqueueJob(JSContext* cx, JS::HandleObject job)
1082 : {
1083 : MOZ_ASSERT(cx->jobQueue);
1084 0 : if (!cx->jobQueue->append(job)) {
1085 : ReportOutOfMemory(cx);
1086 0 : return false;
1087 0 : }
1088 :
1089 0 : return true;
1090 : }
1091 0 :
1092 : JS_FRIEND_API(void)
1093 : js::StopDrainingJobQueue(JSContext* cx)
1094 : {
1095 0 : MOZ_ASSERT(cx->jobQueue);
1096 : cx->stopDrainingJobQueue = true;
1097 0 : }
1098 0 :
1099 0 : JS_FRIEND_API(void)
1100 0 : js::RunJobs(JSContext* cx)
1101 : {
1102 : MOZ_ASSERT(cx->jobQueue);
1103 :
1104 : if (cx->drainingJobQueue || cx->stopDrainingJobQueue)
1105 : return;
1106 :
1107 0 : while (true) {
1108 : cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
1109 0 :
1110 0 : // It doesn't make sense for job queue draining to be reentrant. At the
1111 0 : // same time we don't want to assert against it, because that'd make
1112 : // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
1113 : // so we simply ignore nested calls of drainJobQueue.
1114 0 : cx->drainingJobQueue = true;
1115 :
1116 0 : RootedObject job(cx);
1117 : JS::HandleValueArray args(JS::HandleValueArray::empty());
1118 0 : RootedValue rval(cx);
1119 :
1120 : // Execute jobs in a loop until we've reached the end of the queue.
1121 : // Since executing a job can trigger enqueuing of additional jobs,
1122 0 : // it's crucial to re-check the queue length during each iteration.
1123 : for (size_t i = 0; i < cx->jobQueue->length(); i++) {
1124 : // A previous job might have set this flag. E.g., the js shell
1125 : // sets it if the `quit` builtin function is called.
1126 : if (cx->stopDrainingJobQueue)
1127 : break;
1128 0 :
1129 : job = cx->jobQueue->get()[i];
1130 0 :
1131 0 : // It's possible that queue draining was interrupted prematurely,
1132 0 : // leaving the queue partly processed. In that case, slots for
1133 : // already-executed entries will contain nullptrs, which we should
1134 : // just skip.
1135 : if (!job)
1136 : continue;
1137 0 :
1138 : cx->jobQueue->get()[i] = nullptr;
1139 : AutoRealm ar(cx, job);
1140 0 : {
1141 : if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
1142 : // Nothing we can do about uncatchable exceptions.
1143 0 : if (!cx->isExceptionPending())
1144 : continue;
1145 : RootedValue exn(cx);
1146 : if (cx->getPendingException(&exn)) {
1147 : /*
1148 : * Clear the exception, because
1149 0 : * PrepareScriptEnvironmentAndInvoke will assert that we don't
1150 : * have one.
1151 : */
1152 0 : cx->clearPendingException();
1153 0 : ReportExceptionClosure reportExn(exn);
1154 : PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
1155 0 : }
1156 : }
1157 0 : }
1158 0 : }
1159 0 :
1160 0 : cx->drainingJobQueue = false;
1161 :
1162 : if (cx->stopDrainingJobQueue) {
1163 : cx->stopDrainingJobQueue = false;
1164 : break;
1165 : }
1166 0 :
1167 0 : cx->jobQueue->clear();
1168 0 :
1169 : // It's possible a job added a new off-thread promise task.
1170 : if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending())
1171 : break;
1172 : }
1173 : }
1174 0 :
1175 : JS::Error JSContext::reportedError;
1176 0 : JS::OOM JSContext::reportedOOM;
1177 0 :
1178 0 : mozilla::GenericErrorResult<OOM&>
1179 : JSContext::alreadyReportedOOM()
1180 : {
1181 0 : #ifdef DEBUG
1182 : if (helperThread()) {
1183 : // Keep in sync with addPendingOutOfMemory.
1184 0 : if (ParseTask* task = helperThread()->parseTask())
1185 : MOZ_ASSERT(task->outOfMemory);
1186 : } else {
1187 : MOZ_ASSERT(isThrowingOutOfMemory());
1188 : }
1189 : #endif
1190 : return mozilla::Err(reportedOOM);
1191 : }
1192 :
1193 0 : mozilla::GenericErrorResult<JS::Error&>
1194 : JSContext::alreadyReportedError()
1195 : {
1196 0 : #ifdef DEBUG
1197 : if (!helperThread())
1198 0 : MOZ_ASSERT(isExceptionPending());
1199 0 : #endif
1200 : return mozilla::Err(reportedError);
1201 0 : }
1202 :
1203 : JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
1204 0 : : runtime_(runtime),
1205 : kind_(ContextKind::HelperThread),
1206 : helperThread_(nullptr),
1207 : options_(options),
1208 0 : arenas_(nullptr),
1209 : jitActivation(nullptr),
1210 : activation_(nullptr),
1211 0 : profilingActivation_(nullptr),
1212 0 : nativeStackBase(GetNativeStackBase()),
1213 : entryMonitor(nullptr),
1214 0 : noExecuteDebuggerTop(nullptr),
1215 : activityCallback(nullptr),
1216 : activityCallbackArg(nullptr),
1217 8 : requestDepth(0),
1218 : #ifdef DEBUG
1219 : checkRequestDepth(0),
1220 : inUnsafeCallWithABI(false),
1221 : hasAutoUnsafeCallWithABI(false),
1222 : #endif
1223 : #ifdef JS_SIMULATOR
1224 : simulator_(nullptr),
1225 : #endif
1226 8 : #ifdef JS_TRACE_LOGGING
1227 : traceLogger(nullptr),
1228 : #endif
1229 : autoFlushICache_(nullptr),
1230 : dtoaState(nullptr),
1231 : suppressGC(0),
1232 : #ifdef DEBUG
1233 : ionCompiling(false),
1234 : ionCompilingSafeForMinorGC(false),
1235 : performingGC(false),
1236 : gcSweeping(false),
1237 : gcHelperStateThread(false),
1238 : isTouchingGrayThings(false),
1239 : noGCOrAllocationCheck(0),
1240 : noNurseryAllocationCheck(0),
1241 : disableStrictProxyCheckingCount(0),
1242 : #endif
1243 : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
1244 : runningOOMTest(false),
1245 : #endif
1246 : enableAccessValidation(false),
1247 : inUnsafeRegion(0),
1248 : generationalDisabled(0),
1249 : compactingDisabledCount(0),
1250 : suppressProfilerSampling(false),
1251 : tempLifoAlloc_((size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
1252 : debuggerMutations(0),
1253 : ionPcScriptCache(nullptr),
1254 : throwing(false),
1255 : overRecursed_(false),
1256 : propagatingForcedReturn_(false),
1257 : liveVolatileJitFrameIter_(nullptr),
1258 : reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
1259 : resolvingList(nullptr),
1260 : #ifdef DEBUG
1261 : enteredPolicy(nullptr),
1262 : #endif
1263 : generatingError(false),
1264 : cycleDetectorVector_(this),
1265 : data(nullptr),
1266 : outstandingRequests(0),
1267 : jitIsBroken(false),
1268 : asyncCauseForNewActivations(nullptr),
1269 : asyncCallIsExplicit(false),
1270 : interruptCallbackDisabled(false),
1271 : interruptBits_(0),
1272 : osrTempData_(nullptr),
1273 : ionReturnOverride_(MagicValue(JS_ARG_POISON)),
1274 : jitStackLimit(UINTPTR_MAX),
1275 : jitStackLimitNoInterrupt(UINTPTR_MAX),
1276 : getIncumbentGlobalCallback(nullptr),
1277 : enqueuePromiseJobCallback(nullptr),
1278 : enqueuePromiseJobCallbackData(nullptr),
1279 : jobQueue(nullptr),
1280 : drainingJobQueue(false),
1281 : stopDrainingJobQueue(false),
1282 : promiseRejectionTrackerCallback(nullptr),
1283 : promiseRejectionTrackerCallbackData(nullptr)
1284 : {
1285 : MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
1286 : JS::RootingContext::get(this));
1287 :
1288 8 : MOZ_ASSERT(!TlsContext.get());
1289 : TlsContext.set(this);
1290 :
1291 : for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
1292 : nativeStackQuota[i] = 0;
1293 : }
1294 :
1295 : JSContext::~JSContext()
1296 : {
1297 : // Clear the ContextKind first, so that ProtectedData checks will allow us to
1298 584 : // destroy this context even if the runtime is already gone.
1299 : kind_ = ContextKind::HelperThread;
1300 8 :
1301 : /* Free the stuff hanging off of cx. */
1302 : MOZ_ASSERT(!resolvingList);
1303 0 :
1304 8 : js_delete(ionPcScriptCache.ref());
1305 :
1306 0 : if (dtoaState)
1307 0 : DestroyDtoaState(dtoaState);
1308 8 :
1309 : fx.destroyInstance();
1310 0 : freeOsrTempData();
1311 :
1312 : #ifdef JS_SIMULATOR
1313 : js::jit::Simulator::Destroy(simulator_);
1314 0 : #endif
1315 :
1316 : #ifdef JS_TRACE_LOGGING
1317 0 : if (traceLogger)
1318 : DestroyTraceLogger(traceLogger);
1319 0 : #endif
1320 :
1321 0 : MOZ_ASSERT(TlsContext.get() == this);
1322 0 : TlsContext.set(nullptr);
1323 : }
1324 0 :
1325 0 : void
1326 : JSContext::setRuntime(JSRuntime* rt)
1327 : {
1328 : MOZ_ASSERT(!resolvingList);
1329 : MOZ_ASSERT(!compartment());
1330 : MOZ_ASSERT(!activation());
1331 : MOZ_ASSERT(!unwrappedException_.ref().initialized());
1332 0 : MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized());
1333 0 :
1334 : runtime_ = rt;
1335 : }
1336 0 :
1337 0 : bool
1338 0 : JSContext::getPendingException(MutableHandleValue rval)
1339 : {
1340 : MOZ_ASSERT(throwing);
1341 0 : rval.set(unwrappedException());
1342 : if (zone()->isAtomsZone())
1343 304 : return true;
1344 0 : bool wasOverRecursed = overRecursed_;
1345 152 : clearPendingException();
1346 0 : if (!compartment()->wrap(this, rval))
1347 0 : return false;
1348 : assertSameCompartment(this, rval);
1349 0 : setPendingException(rval);
1350 0 : overRecursed_ = wasOverRecursed;
1351 : return true;
1352 : }
1353 0 :
1354 : bool
1355 302 : JSContext::isThrowingOutOfMemory()
1356 0 : {
1357 302 : return throwing && unwrappedException() == StringValue(names().outOfMemory);
1358 : }
1359 0 :
1360 0 : bool
1361 151 : JSContext::isClosingGenerator()
1362 : {
1363 0 : return throwing && unwrappedException().isMagic(JS_GENERATOR_CLOSING);
1364 0 : }
1365 302 :
1366 0 : bool
1367 : JSContext::isThrowingDebuggeeWouldRun()
1368 : {
1369 : return throwing &&
1370 9 : unwrappedException().isObject() &&
1371 : unwrappedException().toObject().is<ErrorObject>() &&
1372 18 : unwrappedException().toObject().as<ErrorObject>().type() == JSEXN_DEBUGGEEWOULDRUN;
1373 : }
1374 :
1375 : static bool
1376 423 : ComputeIsJITBroken()
1377 : {
1378 846 : #if !defined(ANDROID)
1379 : return false;
1380 : #else // ANDROID
1381 : if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
1382 0 : return false;
1383 : }
1384 0 :
1385 0 : std::string line;
1386 0 :
1387 0 : // Check for the known-bad kernel version (2.6.29).
1388 : std::ifstream osrelease("/proc/sys/kernel/osrelease");
1389 : std::getline(osrelease, line);
1390 : __android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'",
1391 : line.c_str());
1392 :
1393 : if (line.npos == line.find("2.6.29")) {
1394 : // We're using something other than 2.6.29, so the JITs should work.
1395 : __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken");
1396 : return false;
1397 : }
1398 :
1399 : // We're using 2.6.29, and this causes trouble with the JITs on i9000.
1400 : line = "";
1401 : bool broken = false;
1402 : std::ifstream cpuinfo("/proc/cpuinfo");
1403 : do {
1404 : if (0 == line.find("Hardware")) {
1405 : static const char* const blacklist[] = {
1406 : "SCH-I400", // Samsung Continuum
1407 : "SGH-T959", // Samsung i9000, Vibrant device
1408 : "SGH-I897", // Samsung i9000, Captivate device
1409 : "SCH-I500", // Samsung i9000, Fascinate device
1410 : "SPH-D700", // Samsung i9000, Epic device
1411 : "GT-I9000", // Samsung i9000, UK/Europe device
1412 : nullptr
1413 : };
1414 : for (const char* const* hw = &blacklist[0]; *hw; ++hw) {
1415 : if (line.npos != line.find(*hw)) {
1416 : __android_log_print(ANDROID_LOG_INFO, "Gecko",
1417 : "Blacklisted device `%s'", *hw);
1418 : broken = true;
1419 : break;
1420 : }
1421 : }
1422 : break;
1423 : }
1424 : std::getline(cpuinfo, line);
1425 : } while(!cpuinfo.fail() && !cpuinfo.eof());
1426 :
1427 : __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken",
1428 : broken ? "" : "not ");
1429 :
1430 : return broken;
1431 : #endif // ifndef ANDROID
1432 : }
1433 :
1434 : static bool
1435 : IsJITBrokenHere()
1436 : {
1437 : static bool computedIsBroken = false;
1438 : static bool isBroken = false;
1439 : if (!computedIsBroken) {
1440 : isBroken = ComputeIsJITBroken();
1441 : computedIsBroken = true;
1442 : }
1443 : return isBroken;
1444 : }
1445 :
1446 : void
1447 : JSContext::updateJITEnabled()
1448 : {
1449 : jitIsBroken = IsJITBrokenHere();
1450 : }
1451 :
1452 : size_t
1453 : JSContext::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1454 0 : {
1455 0 : /*
1456 0 : * There are other JSContext members that could be measured; the following
1457 : * ones have been found by DMD to be worth measuring. More stuff may be
1458 0 : * added later.
1459 : */
1460 : return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf);
1461 : }
1462 0 :
1463 : #ifdef DEBUG
1464 0 : bool
1465 0 : JSContext::inAtomsZone() const
1466 : {
1467 : return zone_->isAtomsZone();
1468 0 : }
1469 : #endif
1470 :
1471 : void
1472 : JSContext::trace(JSTracer* trc)
1473 : {
1474 : cycleDetectorVector().trace(trc);
1475 0 : geckoProfiler().trace(trc);
1476 :
1477 : if (trc->isMarkingTracer() && realm_)
1478 : realm_->mark();
1479 : }
1480 180133 :
1481 : void*
1482 360266 : JSContext::stackLimitAddressForJitCode(JS::StackKind kind)
1483 : {
1484 : #ifdef JS_SIMULATOR
1485 : return addressOfSimulatorStackLimit();
1486 : #else
1487 4 : return stackLimitAddress(kind);
1488 : #endif
1489 4 : }
1490 0 :
1491 : uintptr_t
1492 0 : JSContext::stackLimitForJitCode(JS::StackKind kind)
1493 0 : {
1494 4 : #ifdef JS_SIMULATOR
1495 : return simulator()->stackLimit();
1496 : #else
1497 0 : return stackLimit(kind);
1498 : #endif
1499 : }
1500 :
1501 : void
1502 0 : JSContext::resetJitStackLimit()
1503 : {
1504 : // Note that, for now, we use the untrusted limit for ion. This is fine,
1505 : // because it's the most conservative limit, and if we hit it, we'll bail
1506 : // out of ion into the interpreter, which will do a proper recursion check.
1507 0 : #ifdef JS_SIMULATOR
1508 : jitStackLimit = jit::Simulator::StackLimit();
1509 : #else
1510 : jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript];
1511 : #endif
1512 0 : jitStackLimitNoInterrupt = jitStackLimit;
1513 : }
1514 :
1515 : void
1516 : JSContext::initJitStackLimit()
1517 55 : {
1518 : resetJitStackLimit();
1519 : }
1520 :
1521 : void
1522 : JSContext::updateMallocCounter(size_t nbytes)
1523 : {
1524 : if (!zone()) {
1525 110 : runtime()->updateMallocCounter(nbytes);
1526 : return;
1527 55 : }
1528 0 :
1529 : zone()->updateMallocCounter(nbytes);
1530 : }
1531 0 :
1532 : #ifdef DEBUG
1533 4 :
1534 0 : JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext* cxArg)
1535 : : cx(cxArg->helperThread() ? nullptr : cxArg)
1536 : {
1537 0 : if (cx) {
1538 : MOZ_ASSERT(cx->requestDepth || JS::RuntimeHeapIsBusy());
1539 54861 : MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1540 0 : cx->checkRequestDepth++;
1541 66 : }
1542 : }
1543 :
1544 0 : JS::AutoCheckRequestDepth::~AutoCheckRequestDepth()
1545 : {
1546 : if (cx) {
1547 : MOZ_ASSERT(cx->checkRequestDepth != 0);
1548 : cx->checkRequestDepth--;
1549 600653 : }
1550 600661 : }
1551 :
1552 0 : #endif
1553 0 :
1554 1192362 : #ifdef JS_CRASH_DIAGNOSTICS
1555 0 : void
1556 : CompartmentChecker::check(InterpreterFrame* fp)
1557 0 : {
1558 : if (fp)
1559 1201280 : check(fp->environmentChain());
1560 : }
1561 600647 :
1562 0 : void
1563 596195 : CompartmentChecker::check(AbstractFramePtr frame)
1564 : {
1565 0 : if (frame)
1566 : check(frame.environmentChain());
1567 : }
1568 : #endif
1569 :
1570 : void
1571 0 : AutoEnterOOMUnsafeRegion::crash(const char* reason)
1572 : {
1573 0 : char msgbuf[1024];
1574 0 : js::NoteIntentionalCrash();
1575 0 : SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
1576 : MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
1577 : MOZ_CRASH();
1578 1 : }
1579 :
1580 25185 : AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
1581 0 : AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback = nullptr;
1582 25185 :
1583 : void
1584 : AutoEnterOOMUnsafeRegion::crash(size_t size, const char* reason)
1585 : {
1586 0 : {
1587 : JS::AutoSuppressGCAnalysis suppress;
1588 : if (annotateOOMSizeCallback)
1589 0 : annotateOOMSizeCallback(size);
1590 0 : }
1591 0 : crash(reason);
1592 0 : }
1593 :
1594 : #ifdef DEBUG
1595 : AutoUnsafeCallWithABI::AutoUnsafeCallWithABI()
1596 : : cx_(TlsContext.get()),
1597 : nested_(cx_->hasAutoUnsafeCallWithABI),
1598 : nogc(cx_)
1599 0 : {
1600 : cx_->hasAutoUnsafeCallWithABI = true;
1601 : }
1602 0 :
1603 0 : AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI()
1604 0 : {
1605 : MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
1606 0 : if (!nested_) {
1607 : cx_->hasAutoUnsafeCallWithABI = false;
1608 : cx_->inUnsafeCallWithABI = false;
1609 : }
1610 109494 : }
1611 109494 : #endif
|