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