|
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 #include "vm/Runtime-inl.h" |
|
8 |
|
9 #include "mozilla/ArrayUtils.h" |
|
10 #include "mozilla/Atomics.h" |
|
11 #include "mozilla/DebugOnly.h" |
|
12 #include "mozilla/MemoryReporting.h" |
|
13 #include "mozilla/ThreadLocal.h" |
|
14 |
|
15 #include <locale.h> |
|
16 #include <string.h> |
|
17 |
|
18 #ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES |
|
19 # include <sys/mman.h> |
|
20 #endif |
|
21 |
|
22 #include "jsatom.h" |
|
23 #include "jsdtoa.h" |
|
24 #include "jsgc.h" |
|
25 #include "jsmath.h" |
|
26 #include "jsnativestack.h" |
|
27 #include "jsobj.h" |
|
28 #include "jsscript.h" |
|
29 #include "jswatchpoint.h" |
|
30 #include "jswrapper.h" |
|
31 |
|
32 #if defined(JS_ION) |
|
33 # include "assembler/assembler/MacroAssembler.h" |
|
34 #endif |
|
35 #include "jit/arm/Simulator-arm.h" |
|
36 #include "jit/AsmJSSignalHandlers.h" |
|
37 #include "jit/JitCompartment.h" |
|
38 #include "jit/PcScriptCache.h" |
|
39 #include "js/MemoryMetrics.h" |
|
40 #include "js/SliceBudget.h" |
|
41 #include "yarr/BumpPointerAllocator.h" |
|
42 |
|
43 #include "jscntxtinlines.h" |
|
44 #include "jsgcinlines.h" |
|
45 |
|
46 using namespace js; |
|
47 using namespace js::gc; |
|
48 |
|
49 using mozilla::Atomic; |
|
50 using mozilla::DebugOnly; |
|
51 using mozilla::NegativeInfinity; |
|
52 using mozilla::PodZero; |
|
53 using mozilla::PodArrayZero; |
|
54 using mozilla::PositiveInfinity; |
|
55 using mozilla::ThreadLocal; |
|
56 using JS::GenericNaN; |
|
57 using JS::DoubleNaNValue; |
|
58 |
|
59 /* static */ ThreadLocal<PerThreadData*> js::TlsPerThreadData; |
|
60 |
|
61 #ifdef JS_THREADSAFE |
|
62 /* static */ Atomic<size_t> JSRuntime::liveRuntimesCount; |
|
63 #else |
|
64 /* static */ size_t JSRuntime::liveRuntimesCount; |
|
65 #endif |
|
66 |
|
67 const JSSecurityCallbacks js::NullSecurityCallbacks = { }; |
|
68 |
|
69 PerThreadData::PerThreadData(JSRuntime *runtime) |
|
70 : PerThreadDataFriendFields(), |
|
71 runtime_(runtime), |
|
72 ionTop(nullptr), |
|
73 jitJSContext(nullptr), |
|
74 jitStackLimit(0), |
|
75 #ifdef JS_TRACE_LOGGING |
|
76 traceLogger(nullptr), |
|
77 #endif |
|
78 activation_(nullptr), |
|
79 asmJSActivationStack_(nullptr), |
|
80 autoFlushICache_(nullptr), |
|
81 #ifdef JS_ARM_SIMULATOR |
|
82 simulator_(nullptr), |
|
83 simulatorStackLimit_(0), |
|
84 #endif |
|
85 dtoaState(nullptr), |
|
86 suppressGC(0), |
|
87 activeCompilations(0) |
|
88 {} |
|
89 |
|
90 PerThreadData::~PerThreadData() |
|
91 { |
|
92 if (dtoaState) |
|
93 js_DestroyDtoaState(dtoaState); |
|
94 |
|
95 #ifdef JS_ARM_SIMULATOR |
|
96 js_delete(simulator_); |
|
97 #endif |
|
98 } |
|
99 |
|
100 bool |
|
101 PerThreadData::init() |
|
102 { |
|
103 dtoaState = js_NewDtoaState(); |
|
104 if (!dtoaState) |
|
105 return false; |
|
106 |
|
107 return true; |
|
108 } |
|
109 |
|
110 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = { |
|
111 TransparentObjectWrapper, |
|
112 nullptr |
|
113 }; |
|
114 |
|
115 JSRuntime::JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThreads) |
|
116 : JS::shadow::Runtime( |
|
117 #ifdef JSGC_GENERATIONAL |
|
118 &gcStoreBuffer |
|
119 #endif |
|
120 ), |
|
121 mainThread(this), |
|
122 parentRuntime(parentRuntime), |
|
123 interrupt(false), |
|
124 #if defined(JS_THREADSAFE) && defined(JS_ION) |
|
125 interruptPar(false), |
|
126 #endif |
|
127 handlingSignal(false), |
|
128 interruptCallback(nullptr), |
|
129 #ifdef JS_THREADSAFE |
|
130 interruptLock(nullptr), |
|
131 interruptLockOwner(nullptr), |
|
132 exclusiveAccessLock(nullptr), |
|
133 exclusiveAccessOwner(nullptr), |
|
134 mainThreadHasExclusiveAccess(false), |
|
135 numExclusiveThreads(0), |
|
136 #else |
|
137 interruptLockTaken(false), |
|
138 #endif |
|
139 systemZone(nullptr), |
|
140 numCompartments(0), |
|
141 localeCallbacks(nullptr), |
|
142 defaultLocale(nullptr), |
|
143 defaultVersion_(JSVERSION_DEFAULT), |
|
144 #ifdef JS_THREADSAFE |
|
145 ownerThread_(nullptr), |
|
146 #endif |
|
147 tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), |
|
148 freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), |
|
149 execAlloc_(nullptr), |
|
150 bumpAlloc_(nullptr), |
|
151 jitRuntime_(nullptr), |
|
152 selfHostingGlobal_(nullptr), |
|
153 nativeStackBase(0), |
|
154 cxCallback(nullptr), |
|
155 destroyCompartmentCallback(nullptr), |
|
156 destroyZoneCallback(nullptr), |
|
157 sweepZoneCallback(nullptr), |
|
158 compartmentNameCallback(nullptr), |
|
159 activityCallback(nullptr), |
|
160 activityCallbackArg(nullptr), |
|
161 #ifdef JS_THREADSAFE |
|
162 requestDepth(0), |
|
163 # ifdef DEBUG |
|
164 checkRequestDepth(0), |
|
165 # endif |
|
166 #endif |
|
167 #ifdef DEBUG |
|
168 activeContext(nullptr), |
|
169 #endif |
|
170 gcInitialized(false), |
|
171 gcSystemAvailableChunkListHead(nullptr), |
|
172 gcUserAvailableChunkListHead(nullptr), |
|
173 gcBytes(0), |
|
174 gcMaxBytes(0), |
|
175 gcMaxMallocBytes(0), |
|
176 gcNumArenasFreeCommitted(0), |
|
177 gcMarker(this), |
|
178 gcVerifyPreData(nullptr), |
|
179 gcVerifyPostData(nullptr), |
|
180 gcChunkAllocationSinceLastGC(false), |
|
181 gcNextFullGCTime(0), |
|
182 gcLastGCTime(0), |
|
183 gcJitReleaseTime(0), |
|
184 gcAllocationThreshold(30 * 1024 * 1024), |
|
185 gcHighFrequencyGC(false), |
|
186 gcHighFrequencyTimeThreshold(1000), |
|
187 gcHighFrequencyLowLimitBytes(100 * 1024 * 1024), |
|
188 gcHighFrequencyHighLimitBytes(500 * 1024 * 1024), |
|
189 gcHighFrequencyHeapGrowthMax(3.0), |
|
190 gcHighFrequencyHeapGrowthMin(1.5), |
|
191 gcLowFrequencyHeapGrowth(1.5), |
|
192 gcDynamicHeapGrowth(false), |
|
193 gcDynamicMarkSlice(false), |
|
194 gcDecommitThreshold(32 * 1024 * 1024), |
|
195 gcShouldCleanUpEverything(false), |
|
196 gcGrayBitsValid(false), |
|
197 gcIsNeeded(0), |
|
198 gcStats(thisFromCtor()), |
|
199 gcNumber(0), |
|
200 gcStartNumber(0), |
|
201 gcIsFull(false), |
|
202 gcTriggerReason(JS::gcreason::NO_REASON), |
|
203 gcStrictCompartmentChecking(false), |
|
204 #ifdef DEBUG |
|
205 gcDisableStrictProxyCheckingCount(0), |
|
206 #endif |
|
207 gcIncrementalState(gc::NO_INCREMENTAL), |
|
208 gcLastMarkSlice(false), |
|
209 gcSweepOnBackgroundThread(false), |
|
210 gcFoundBlackGrayEdges(false), |
|
211 gcSweepingZones(nullptr), |
|
212 gcZoneGroupIndex(0), |
|
213 gcZoneGroups(nullptr), |
|
214 gcCurrentZoneGroup(nullptr), |
|
215 gcSweepPhase(0), |
|
216 gcSweepZone(nullptr), |
|
217 gcSweepKindIndex(0), |
|
218 gcAbortSweepAfterCurrentGroup(false), |
|
219 gcArenasAllocatedDuringSweep(nullptr), |
|
220 #ifdef DEBUG |
|
221 gcMarkingValidator(nullptr), |
|
222 #endif |
|
223 gcInterFrameGC(0), |
|
224 gcSliceBudget(SliceBudget::Unlimited), |
|
225 gcIncrementalEnabled(true), |
|
226 gcGenerationalDisabled(0), |
|
227 gcManipulatingDeadZones(false), |
|
228 gcObjectsMarkedInDeadZones(0), |
|
229 gcPoke(false), |
|
230 heapState(Idle), |
|
231 #ifdef JSGC_GENERATIONAL |
|
232 gcNursery(thisFromCtor()), |
|
233 gcStoreBuffer(thisFromCtor(), gcNursery), |
|
234 #endif |
|
235 #ifdef JS_GC_ZEAL |
|
236 gcZeal_(0), |
|
237 gcZealFrequency(0), |
|
238 gcNextScheduled(0), |
|
239 gcDeterministicOnly(false), |
|
240 gcIncrementalLimit(0), |
|
241 #endif |
|
242 gcValidate(true), |
|
243 gcFullCompartmentChecks(false), |
|
244 gcCallback(nullptr), |
|
245 gcSliceCallback(nullptr), |
|
246 gcFinalizeCallback(nullptr), |
|
247 gcMallocBytes(0), |
|
248 gcMallocGCTriggered(false), |
|
249 #ifdef JS_ARM_SIMULATOR |
|
250 simulatorRuntime_(nullptr), |
|
251 #endif |
|
252 scriptAndCountsVector(nullptr), |
|
253 NaNValue(DoubleNaNValue()), |
|
254 negativeInfinityValue(DoubleValue(NegativeInfinity<double>())), |
|
255 positiveInfinityValue(DoubleValue(PositiveInfinity<double>())), |
|
256 emptyString(nullptr), |
|
257 debugMode(false), |
|
258 spsProfiler(thisFromCtor()), |
|
259 profilingScripts(false), |
|
260 alwaysPreserveCode(false), |
|
261 hadOutOfMemory(false), |
|
262 haveCreatedContext(false), |
|
263 data(nullptr), |
|
264 gcLock(nullptr), |
|
265 gcLockOwner(nullptr), |
|
266 gcHelperThread(thisFromCtor()), |
|
267 signalHandlersInstalled_(false), |
|
268 defaultFreeOp_(thisFromCtor(), false), |
|
269 debuggerMutations(0), |
|
270 securityCallbacks(const_cast<JSSecurityCallbacks *>(&NullSecurityCallbacks)), |
|
271 DOMcallbacks(nullptr), |
|
272 destroyPrincipals(nullptr), |
|
273 structuredCloneCallbacks(nullptr), |
|
274 telemetryCallback(nullptr), |
|
275 propertyRemovals(0), |
|
276 #if !EXPOSE_INTL_API |
|
277 thousandsSeparator(0), |
|
278 decimalSeparator(0), |
|
279 numGrouping(0), |
|
280 #endif |
|
281 mathCache_(nullptr), |
|
282 activeCompilations_(0), |
|
283 keepAtoms_(0), |
|
284 trustedPrincipals_(nullptr), |
|
285 beingDestroyed_(false), |
|
286 atoms_(nullptr), |
|
287 atomsCompartment_(nullptr), |
|
288 staticStrings(nullptr), |
|
289 commonNames(nullptr), |
|
290 permanentAtoms(nullptr), |
|
291 wrapObjectCallbacks(&DefaultWrapObjectCallbacks), |
|
292 preserveWrapperCallback(nullptr), |
|
293 #ifdef DEBUG |
|
294 noGCOrAllocationCheck(0), |
|
295 #endif |
|
296 jitSupportsFloatingPoint(false), |
|
297 ionPcScriptCache(nullptr), |
|
298 threadPool(this), |
|
299 defaultJSContextCallback(nullptr), |
|
300 ctypesActivityCallback(nullptr), |
|
301 forkJoinWarmup(0), |
|
302 ionReturnOverride_(MagicValue(JS_ARG_POISON)), |
|
303 useHelperThreads_(useHelperThreads), |
|
304 parallelIonCompilationEnabled_(true), |
|
305 parallelParsingEnabled_(true), |
|
306 isWorkerRuntime_(false), |
|
307 #ifdef DEBUG |
|
308 enteredPolicy(nullptr), |
|
309 #endif |
|
310 largeAllocationFailureCallback(nullptr), |
|
311 oomCallback(nullptr) |
|
312 { |
|
313 liveRuntimesCount++; |
|
314 |
|
315 setGCMode(JSGC_MODE_GLOBAL); |
|
316 |
|
317 /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ |
|
318 JS_INIT_CLIST(&onNewGlobalObjectWatchers); |
|
319 |
|
320 PodZero(&debugHooks); |
|
321 PodArrayZero(nativeStackQuota); |
|
322 PodZero(&asmJSCacheOps); |
|
323 } |
|
324 |
|
325 static bool |
|
326 JitSupportsFloatingPoint() |
|
327 { |
|
328 #if defined(JS_ION) |
|
329 if (!JSC::MacroAssembler::supportsFloatingPoint()) |
|
330 return false; |
|
331 |
|
332 #if defined(JS_ION) && WTF_ARM_ARCH_VERSION == 6 |
|
333 if (!js::jit::hasVFP()) |
|
334 return false; |
|
335 #endif |
|
336 |
|
337 return true; |
|
338 #else |
|
339 return false; |
|
340 #endif |
|
341 } |
|
342 |
|
343 bool |
|
344 JSRuntime::init(uint32_t maxbytes) |
|
345 { |
|
346 #ifdef JS_THREADSAFE |
|
347 ownerThread_ = PR_GetCurrentThread(); |
|
348 |
|
349 interruptLock = PR_NewLock(); |
|
350 if (!interruptLock) |
|
351 return false; |
|
352 |
|
353 gcLock = PR_NewLock(); |
|
354 if (!gcLock) |
|
355 return false; |
|
356 |
|
357 exclusiveAccessLock = PR_NewLock(); |
|
358 if (!exclusiveAccessLock) |
|
359 return false; |
|
360 #endif |
|
361 |
|
362 if (!mainThread.init()) |
|
363 return false; |
|
364 |
|
365 js::TlsPerThreadData.set(&mainThread); |
|
366 |
|
367 if (!threadPool.init()) |
|
368 return false; |
|
369 |
|
370 if (!js_InitGC(this, maxbytes)) |
|
371 return false; |
|
372 |
|
373 if (!gcMarker.init(gcMode())) |
|
374 return false; |
|
375 |
|
376 const char *size = getenv("JSGC_MARK_STACK_LIMIT"); |
|
377 if (size) |
|
378 SetMarkStackLimit(this, atoi(size)); |
|
379 |
|
380 ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this)); |
|
381 if (!atomsZone) |
|
382 return false; |
|
383 |
|
384 JS::CompartmentOptions options; |
|
385 ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options)); |
|
386 if (!atomsCompartment || !atomsCompartment->init(nullptr)) |
|
387 return false; |
|
388 |
|
389 zones.append(atomsZone.get()); |
|
390 atomsZone->compartments.append(atomsCompartment.get()); |
|
391 |
|
392 atomsCompartment->isSystem = true; |
|
393 atomsZone->isSystem = true; |
|
394 atomsZone->setGCLastBytes(8192, GC_NORMAL); |
|
395 |
|
396 atomsZone.forget(); |
|
397 this->atomsCompartment_ = atomsCompartment.forget(); |
|
398 |
|
399 if (!scriptDataTable_.init()) |
|
400 return false; |
|
401 |
|
402 if (!evalCache.init()) |
|
403 return false; |
|
404 |
|
405 /* The garbage collector depends on everything before this point being initialized. */ |
|
406 gcInitialized = true; |
|
407 |
|
408 if (!InitRuntimeNumberState(this)) |
|
409 return false; |
|
410 |
|
411 dateTimeInfo.updateTimeZoneAdjustment(); |
|
412 |
|
413 #ifdef JS_ARM_SIMULATOR |
|
414 simulatorRuntime_ = js::jit::CreateSimulatorRuntime(); |
|
415 if (!simulatorRuntime_) |
|
416 return false; |
|
417 #endif |
|
418 |
|
419 nativeStackBase = GetNativeStackBase(); |
|
420 |
|
421 jitSupportsFloatingPoint = JitSupportsFloatingPoint(); |
|
422 |
|
423 #ifdef JS_ION |
|
424 signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this); |
|
425 #endif |
|
426 |
|
427 if (!spsProfiler.init()) |
|
428 return false; |
|
429 |
|
430 return true; |
|
431 } |
|
432 |
|
433 JSRuntime::~JSRuntime() |
|
434 { |
|
435 JS_ASSERT(!isHeapBusy()); |
|
436 |
|
437 if (gcInitialized) { |
|
438 /* Free source hook early, as its destructor may want to delete roots. */ |
|
439 sourceHook = nullptr; |
|
440 |
|
441 /* |
|
442 * Cancel any pending, in progress or completed Ion compilations and |
|
443 * parse tasks. Waiting for AsmJS and compression tasks is done |
|
444 * synchronously (on the main thread or during parse tasks), so no |
|
445 * explicit canceling is needed for these. |
|
446 */ |
|
447 for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) |
|
448 CancelOffThreadIonCompile(comp, nullptr); |
|
449 CancelOffThreadParses(this); |
|
450 |
|
451 /* Clear debugging state to remove GC roots. */ |
|
452 for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) { |
|
453 comp->clearTraps(defaultFreeOp()); |
|
454 if (WatchpointMap *wpmap = comp->watchpointMap) |
|
455 wpmap->clear(); |
|
456 } |
|
457 |
|
458 /* Clear atoms to remove GC roots and heap allocations. */ |
|
459 finishAtoms(); |
|
460 |
|
461 /* |
|
462 * Flag us as being destroyed. This allows the GC to free things like |
|
463 * interned atoms and Ion trampolines. |
|
464 */ |
|
465 beingDestroyed_ = true; |
|
466 |
|
467 /* Allow the GC to release scripts that were being profiled. */ |
|
468 profilingScripts = false; |
|
469 |
|
470 JS::PrepareForFullGC(this); |
|
471 GC(this, GC_NORMAL, JS::gcreason::DESTROY_RUNTIME); |
|
472 } |
|
473 |
|
474 /* |
|
475 * Clear the self-hosted global and delete self-hosted classes *after* |
|
476 * GC, as finalizers for objects check for clasp->finalize during GC. |
|
477 */ |
|
478 finishSelfHosting(); |
|
479 |
|
480 #ifdef JS_THREADSAFE |
|
481 JS_ASSERT(!exclusiveAccessOwner); |
|
482 if (exclusiveAccessLock) |
|
483 PR_DestroyLock(exclusiveAccessLock); |
|
484 |
|
485 // Avoid bogus asserts during teardown. |
|
486 JS_ASSERT(!numExclusiveThreads); |
|
487 mainThreadHasExclusiveAccess = true; |
|
488 |
|
489 JS_ASSERT(!interruptLockOwner); |
|
490 if (interruptLock) |
|
491 PR_DestroyLock(interruptLock); |
|
492 #endif |
|
493 |
|
494 /* |
|
495 * Even though all objects in the compartment are dead, we may have keep |
|
496 * some filenames around because of gcKeepAtoms. |
|
497 */ |
|
498 FreeScriptData(this); |
|
499 |
|
500 #ifdef DEBUG |
|
501 /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ |
|
502 if (hasContexts()) { |
|
503 unsigned cxcount = 0; |
|
504 for (ContextIter acx(this); !acx.done(); acx.next()) { |
|
505 fprintf(stderr, |
|
506 "JS API usage error: found live context at %p\n", |
|
507 (void *) acx.get()); |
|
508 cxcount++; |
|
509 } |
|
510 fprintf(stderr, |
|
511 "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n", |
|
512 cxcount, (cxcount == 1) ? "" : "s"); |
|
513 } |
|
514 #endif |
|
515 |
|
516 #if !EXPOSE_INTL_API |
|
517 FinishRuntimeNumberState(this); |
|
518 #endif |
|
519 |
|
520 js_FinishGC(this); |
|
521 atomsCompartment_ = nullptr; |
|
522 |
|
523 #ifdef JS_THREADSAFE |
|
524 if (gcLock) |
|
525 PR_DestroyLock(gcLock); |
|
526 #endif |
|
527 |
|
528 js_free(defaultLocale); |
|
529 js_delete(bumpAlloc_); |
|
530 js_delete(mathCache_); |
|
531 #ifdef JS_ION |
|
532 js_delete(jitRuntime_); |
|
533 #endif |
|
534 js_delete(execAlloc_); /* Delete after jitRuntime_. */ |
|
535 |
|
536 js_delete(ionPcScriptCache); |
|
537 |
|
538 #ifdef JSGC_GENERATIONAL |
|
539 gcStoreBuffer.disable(); |
|
540 gcNursery.disable(); |
|
541 #endif |
|
542 |
|
543 #ifdef JS_ARM_SIMULATOR |
|
544 js::jit::DestroySimulatorRuntime(simulatorRuntime_); |
|
545 #endif |
|
546 |
|
547 DebugOnly<size_t> oldCount = liveRuntimesCount--; |
|
548 JS_ASSERT(oldCount > 0); |
|
549 |
|
550 #ifdef JS_THREADSAFE |
|
551 js::TlsPerThreadData.set(nullptr); |
|
552 #endif |
|
553 } |
|
554 |
|
555 void |
|
556 NewObjectCache::clearNurseryObjects(JSRuntime *rt) |
|
557 { |
|
558 for (unsigned i = 0; i < mozilla::ArrayLength(entries); ++i) { |
|
559 Entry &e = entries[i]; |
|
560 JSObject *obj = reinterpret_cast<JSObject *>(&e.templateObject); |
|
561 if (IsInsideNursery(rt, e.key) || |
|
562 IsInsideNursery(rt, obj->slots) || |
|
563 IsInsideNursery(rt, obj->elements)) |
|
564 { |
|
565 PodZero(&e); |
|
566 } |
|
567 } |
|
568 } |
|
569 |
|
570 void |
|
571 JSRuntime::resetJitStackLimit() |
|
572 { |
|
573 AutoLockForInterrupt lock(this); |
|
574 mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]); |
|
575 |
|
576 #ifdef JS_ARM_SIMULATOR |
|
577 mainThread.setJitStackLimit(js::jit::Simulator::StackLimit()); |
|
578 #endif |
|
579 } |
|
580 |
|
581 void |
|
582 JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes) |
|
583 { |
|
584 // Several tables in the runtime enumerated below can be used off thread. |
|
585 AutoLockForExclusiveAccess lock(this); |
|
586 |
|
587 rtSizes->object += mallocSizeOf(this); |
|
588 |
|
589 rtSizes->atomsTable += atoms().sizeOfIncludingThis(mallocSizeOf); |
|
590 |
|
591 if (!parentRuntime) { |
|
592 rtSizes->atomsTable += mallocSizeOf(staticStrings); |
|
593 rtSizes->atomsTable += mallocSizeOf(commonNames); |
|
594 rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf); |
|
595 } |
|
596 |
|
597 for (ContextIter acx(this); !acx.done(); acx.next()) |
|
598 rtSizes->contexts += acx->sizeOfIncludingThis(mallocSizeOf); |
|
599 |
|
600 rtSizes->dtoa += mallocSizeOf(mainThread.dtoaState); |
|
601 |
|
602 rtSizes->temporary += tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); |
|
603 |
|
604 rtSizes->regexpData += bumpAlloc_ ? bumpAlloc_->sizeOfNonHeapData() : 0; |
|
605 |
|
606 rtSizes->interpreterStack += interpreterStack_.sizeOfExcludingThis(mallocSizeOf); |
|
607 |
|
608 rtSizes->mathCache += mathCache_ ? mathCache_->sizeOfIncludingThis(mallocSizeOf) : 0; |
|
609 |
|
610 rtSizes->sourceDataCache += sourceDataCache.sizeOfExcludingThis(mallocSizeOf); |
|
611 |
|
612 rtSizes->scriptData += scriptDataTable().sizeOfExcludingThis(mallocSizeOf); |
|
613 for (ScriptDataTable::Range r = scriptDataTable().all(); !r.empty(); r.popFront()) |
|
614 rtSizes->scriptData += mallocSizeOf(r.front()); |
|
615 |
|
616 if (execAlloc_) |
|
617 execAlloc_->addSizeOfCode(&rtSizes->code); |
|
618 #ifdef JS_ION |
|
619 { |
|
620 AutoLockForInterrupt lock(this); |
|
621 if (jitRuntime()) { |
|
622 if (JSC::ExecutableAllocator *ionAlloc = jitRuntime()->ionAlloc(this)) |
|
623 ionAlloc->addSizeOfCode(&rtSizes->code); |
|
624 } |
|
625 } |
|
626 #endif |
|
627 |
|
628 rtSizes->gc.marker += gcMarker.sizeOfExcludingThis(mallocSizeOf); |
|
629 #ifdef JSGC_GENERATIONAL |
|
630 rtSizes->gc.nurseryCommitted += gcNursery.sizeOfHeapCommitted(); |
|
631 rtSizes->gc.nurseryDecommitted += gcNursery.sizeOfHeapDecommitted(); |
|
632 rtSizes->gc.nurseryHugeSlots += gcNursery.sizeOfHugeSlots(mallocSizeOf); |
|
633 gcStoreBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); |
|
634 #endif |
|
635 } |
|
636 |
|
637 static bool |
|
638 SignalBasedTriggersDisabled() |
|
639 { |
|
640 // Don't bother trying to cache the getenv lookup; this should be called |
|
641 // infrequently. |
|
642 return !!getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS"); |
|
643 } |
|
644 |
|
645 void |
|
646 JSRuntime::requestInterrupt(InterruptMode mode) |
|
647 { |
|
648 AutoLockForInterrupt lock(this); |
|
649 |
|
650 /* |
|
651 * Invalidate ionTop to trigger its over-recursion check. Note this must be |
|
652 * set before interrupt, to avoid racing with js::InvokeInterruptCallback, |
|
653 * into a weird state where interrupt is stuck at 0 but jitStackLimit is |
|
654 * MAXADDR. |
|
655 */ |
|
656 mainThread.setJitStackLimit(-1); |
|
657 |
|
658 interrupt = true; |
|
659 |
|
660 #ifdef JS_ION |
|
661 #ifdef JS_THREADSAFE |
|
662 RequestInterruptForForkJoin(this, mode); |
|
663 #endif |
|
664 |
|
665 /* |
|
666 * asm.js and, optionally, normal Ion code use memory protection and signal |
|
667 * handlers to halt running code. |
|
668 */ |
|
669 if (!SignalBasedTriggersDisabled()) { |
|
670 RequestInterruptForAsmJSCode(this); |
|
671 jit::RequestInterruptForIonCode(this, mode); |
|
672 } |
|
673 #endif |
|
674 } |
|
675 |
|
676 JSC::ExecutableAllocator * |
|
677 JSRuntime::createExecutableAllocator(JSContext *cx) |
|
678 { |
|
679 JS_ASSERT(!execAlloc_); |
|
680 JS_ASSERT(cx->runtime() == this); |
|
681 |
|
682 execAlloc_ = js_new<JSC::ExecutableAllocator>(); |
|
683 if (!execAlloc_) |
|
684 js_ReportOutOfMemory(cx); |
|
685 return execAlloc_; |
|
686 } |
|
687 |
|
688 WTF::BumpPointerAllocator * |
|
689 JSRuntime::createBumpPointerAllocator(JSContext *cx) |
|
690 { |
|
691 JS_ASSERT(!bumpAlloc_); |
|
692 JS_ASSERT(cx->runtime() == this); |
|
693 |
|
694 bumpAlloc_ = js_new<WTF::BumpPointerAllocator>(); |
|
695 if (!bumpAlloc_) |
|
696 js_ReportOutOfMemory(cx); |
|
697 return bumpAlloc_; |
|
698 } |
|
699 |
|
700 MathCache * |
|
701 JSRuntime::createMathCache(JSContext *cx) |
|
702 { |
|
703 JS_ASSERT(!mathCache_); |
|
704 JS_ASSERT(cx->runtime() == this); |
|
705 |
|
706 MathCache *newMathCache = js_new<MathCache>(); |
|
707 if (!newMathCache) { |
|
708 js_ReportOutOfMemory(cx); |
|
709 return nullptr; |
|
710 } |
|
711 |
|
712 mathCache_ = newMathCache; |
|
713 return mathCache_; |
|
714 } |
|
715 |
|
716 bool |
|
717 JSRuntime::setDefaultLocale(const char *locale) |
|
718 { |
|
719 if (!locale) |
|
720 return false; |
|
721 resetDefaultLocale(); |
|
722 defaultLocale = JS_strdup(this, locale); |
|
723 return defaultLocale != nullptr; |
|
724 } |
|
725 |
|
726 void |
|
727 JSRuntime::resetDefaultLocale() |
|
728 { |
|
729 js_free(defaultLocale); |
|
730 defaultLocale = nullptr; |
|
731 } |
|
732 |
|
733 const char * |
|
734 JSRuntime::getDefaultLocale() |
|
735 { |
|
736 if (defaultLocale) |
|
737 return defaultLocale; |
|
738 |
|
739 char *locale, *lang, *p; |
|
740 #ifdef HAVE_SETLOCALE |
|
741 locale = setlocale(LC_ALL, nullptr); |
|
742 #else |
|
743 locale = getenv("LANG"); |
|
744 #endif |
|
745 // convert to a well-formed BCP 47 language tag |
|
746 if (!locale || !strcmp(locale, "C")) |
|
747 locale = const_cast<char*>("und"); |
|
748 lang = JS_strdup(this, locale); |
|
749 if (!lang) |
|
750 return nullptr; |
|
751 if ((p = strchr(lang, '.'))) |
|
752 *p = '\0'; |
|
753 while ((p = strchr(lang, '_'))) |
|
754 *p = '-'; |
|
755 |
|
756 defaultLocale = lang; |
|
757 return defaultLocale; |
|
758 } |
|
759 |
|
760 void |
|
761 JSRuntime::triggerActivityCallback(bool active) |
|
762 { |
|
763 if (!activityCallback) |
|
764 return; |
|
765 |
|
766 /* |
|
767 * The activity callback must not trigger a GC: it would create a cirular |
|
768 * dependency between entering a request and Rooted's requirement of being |
|
769 * in a request. In practice this callback already cannot trigger GC. The |
|
770 * suppression serves to inform the exact rooting hazard analysis of this |
|
771 * property and ensures that it remains true in the future. |
|
772 */ |
|
773 AutoSuppressGC suppress(this); |
|
774 |
|
775 activityCallback(activityCallbackArg, active); |
|
776 } |
|
777 |
|
778 void |
|
779 JSRuntime::setGCMaxMallocBytes(size_t value) |
|
780 { |
|
781 /* |
|
782 * For compatibility treat any value that exceeds PTRDIFF_T_MAX to |
|
783 * mean that value. |
|
784 */ |
|
785 gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; |
|
786 resetGCMallocBytes(); |
|
787 for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) |
|
788 zone->setGCMaxMallocBytes(value); |
|
789 } |
|
790 |
|
791 void |
|
792 JSRuntime::updateMallocCounter(size_t nbytes) |
|
793 { |
|
794 updateMallocCounter(nullptr, nbytes); |
|
795 } |
|
796 |
|
797 void |
|
798 JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes) |
|
799 { |
|
800 /* We tolerate any thread races when updating gcMallocBytes. */ |
|
801 gcMallocBytes -= ptrdiff_t(nbytes); |
|
802 if (MOZ_UNLIKELY(gcMallocBytes <= 0)) |
|
803 onTooMuchMalloc(); |
|
804 else if (zone) |
|
805 zone->updateMallocCounter(nbytes); |
|
806 } |
|
807 |
|
808 JS_FRIEND_API(void) |
|
809 JSRuntime::onTooMuchMalloc() |
|
810 { |
|
811 if (!CurrentThreadCanAccessRuntime(this)) |
|
812 return; |
|
813 |
|
814 if (!gcMallocGCTriggered) |
|
815 gcMallocGCTriggered = TriggerGC(this, JS::gcreason::TOO_MUCH_MALLOC); |
|
816 } |
|
817 |
|
818 JS_FRIEND_API(void *) |
|
819 JSRuntime::onOutOfMemory(void *p, size_t nbytes) |
|
820 { |
|
821 return onOutOfMemory(p, nbytes, nullptr); |
|
822 } |
|
823 |
|
824 JS_FRIEND_API(void *) |
|
825 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) |
|
826 { |
|
827 if (isHeapBusy()) |
|
828 return nullptr; |
|
829 |
|
830 /* |
|
831 * Retry when we are done with the background sweeping and have stopped |
|
832 * all the allocations and released the empty GC chunks. |
|
833 */ |
|
834 JS::ShrinkGCBuffers(this); |
|
835 gcHelperThread.waitBackgroundSweepOrAllocEnd(); |
|
836 if (!p) |
|
837 p = js_malloc(nbytes); |
|
838 else if (p == reinterpret_cast<void *>(1)) |
|
839 p = js_calloc(nbytes); |
|
840 else |
|
841 p = js_realloc(p, nbytes); |
|
842 if (p) |
|
843 return p; |
|
844 if (cx) |
|
845 js_ReportOutOfMemory(cx); |
|
846 return nullptr; |
|
847 } |
|
848 |
|
849 bool |
|
850 JSRuntime::activeGCInAtomsZone() |
|
851 { |
|
852 Zone *zone = atomsCompartment_->zone(); |
|
853 return zone->needsBarrier() || zone->isGCScheduled() || zone->wasGCStarted(); |
|
854 } |
|
855 |
|
856 #ifdef JS_THREADSAFE |
|
857 |
|
858 void |
|
859 JSRuntime::setUsedByExclusiveThread(Zone *zone) |
|
860 { |
|
861 JS_ASSERT(!zone->usedByExclusiveThread); |
|
862 zone->usedByExclusiveThread = true; |
|
863 numExclusiveThreads++; |
|
864 } |
|
865 |
|
866 void |
|
867 JSRuntime::clearUsedByExclusiveThread(Zone *zone) |
|
868 { |
|
869 JS_ASSERT(zone->usedByExclusiveThread); |
|
870 zone->usedByExclusiveThread = false; |
|
871 numExclusiveThreads--; |
|
872 } |
|
873 |
|
874 bool |
|
875 js::CurrentThreadCanAccessRuntime(JSRuntime *rt) |
|
876 { |
|
877 return rt->ownerThread_ == PR_GetCurrentThread() && !InParallelSection(); |
|
878 } |
|
879 |
|
880 bool |
|
881 js::CurrentThreadCanAccessZone(Zone *zone) |
|
882 { |
|
883 if (CurrentThreadCanAccessRuntime(zone->runtime_)) |
|
884 return true; |
|
885 if (InParallelSection()) { |
|
886 DebugOnly<PerThreadData *> pt = js::TlsPerThreadData.get(); |
|
887 JS_ASSERT(pt && pt->associatedWith(zone->runtime_)); |
|
888 return true; |
|
889 } |
|
890 |
|
891 // Only zones in use by an exclusive thread can be used off the main thread |
|
892 // or outside of PJS. We don't keep track of which thread owns such zones |
|
893 // though, so this check is imperfect. |
|
894 return zone->usedByExclusiveThread; |
|
895 } |
|
896 |
|
897 #else // JS_THREADSAFE |
|
898 |
|
899 bool |
|
900 js::CurrentThreadCanAccessRuntime(JSRuntime *rt) |
|
901 { |
|
902 return true; |
|
903 } |
|
904 |
|
905 bool |
|
906 js::CurrentThreadCanAccessZone(Zone *zone) |
|
907 { |
|
908 return true; |
|
909 } |
|
910 |
|
911 #endif // JS_THREADSAFE |
|
912 |
|
913 #ifdef DEBUG |
|
914 |
|
915 void |
|
916 JSRuntime::assertCanLock(RuntimeLock which) |
|
917 { |
|
918 #ifdef JS_THREADSAFE |
|
919 // In the switch below, each case falls through to the one below it. None |
|
920 // of the runtime locks are reentrant, and when multiple locks are acquired |
|
921 // it must be done in the order below. |
|
922 switch (which) { |
|
923 case ExclusiveAccessLock: |
|
924 JS_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread()); |
|
925 case WorkerThreadStateLock: |
|
926 JS_ASSERT(!WorkerThreadState().isLocked()); |
|
927 case InterruptLock: |
|
928 JS_ASSERT(!currentThreadOwnsInterruptLock()); |
|
929 case GCLock: |
|
930 JS_ASSERT(gcLockOwner != PR_GetCurrentThread()); |
|
931 break; |
|
932 default: |
|
933 MOZ_CRASH(); |
|
934 } |
|
935 #endif // JS_THREADSAFE |
|
936 } |
|
937 |
|
938 void |
|
939 js::AssertCurrentThreadCanLock(RuntimeLock which) |
|
940 { |
|
941 #ifdef JS_THREADSAFE |
|
942 PerThreadData *pt = TlsPerThreadData.get(); |
|
943 if (pt && pt->runtime_) |
|
944 pt->runtime_->assertCanLock(which); |
|
945 #endif |
|
946 } |
|
947 |
|
948 #endif // DEBUG |