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