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