js/src/jscntxt.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

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

mercurial