mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
f18df9acf8
- Bug 1239496 - Load the UA sheet lazily so we can invalidate it for pref changes. Make it depend on the "layout.css.grid.enabled" pref. r=heycam (e4a1ac18b4)
- Bug 1225192 - fix eslint complaints about css-logic.js; r=pbrosset (ef19819c21)
- Bug 1225192 - remove leading whitespace before prettifying css; r=pbrosset (6b39d08907)
- Bug 1230491 - rewrite CssLogic.isContentStylesheet; r=bgrins (14c86cee67)
- Bug 1230491 - add CSSStyleSheet::parsingMode; r=heycam,bz (9ecabe232e)
- Bug 1239336 - set parsing mode on about:PreferenceStyleSheet; r=heycam,bgrins (cac07e099f)
- Bug 1224433 - Part 1: Add reftest. r=roc (28c8a47812)
- Bug 1195491 - use nsCOMPtr in RDFContentSinkImpl; r=bsmedberg (9804abe22e)
- Bug 1205713 - Merge ImageHostOverlay/ImageClientOverlay to ImageHost/ImageClient r=nical (2871e70466)
- Bug 1234472 - Add gonk sideband stream handling to gfx ipc r=nical (559f788a10)
- Bug 1049296 - Handle Sideband stream compositing in HwcComposer2D r=mwu,nical (b6dbac1a1b)
- Bug 1237508 - Odin: move GC allocation up to fix spurious hazard warning (there was an AutoKeepAtoms already) (r=hazard-red) (b5d0e54993)
- Bug 1229399: Group all IR's enums into a single one; r=luke (a7cfedc16a)
- Bug 1229399: Make {Get,Set}{Loc,Glo} opcodes type-independent; r=luke (0ed8e44098)
- Bug 1239211 - Odin: tweak wasm::Decoder (r=bbouvier) (980e08dc1e)
- Bug 1229399: Implement write/readVarU32 and use it for locals/globals; r=luke (5a6f654a14)
- Bug 1229399: Unite all the Emit* functions in WasmIonCompile; r=luke (64e517d18f)
- Bug 1240524: Fix AsmJS checkedValueType of Bool32x4; r=luke (d98ee763c7)
- Bug 1229399: Add list of wasm opcodes described in v8 design document; r=luke (1e05832624)
- Bug 1241339 - ObserverService may keep Console object alive until shutdown, r=baku (330d9e7bc0)
- Bug 1239177 - Odin: make calls more like wasm (r=bbouvier) (4bab27c7bc)
- Bug 1234985 - Odin: hoist read/writeOp into wasm::Decoder/Encoder (r=bbouvier) (de3ba3c3f9)
- Bug 1229399: Add test file forgotten in previous rebase/checkin; r=bustage (a2bdda0e86)
- Bug 1234985 - Odin: tweak ModuleGenerator interface for Baldr use (r=bbouvier) (5f52fd6777)
- Bug 1234985 - BaldrMonkey: testing-only (not content visible) wasm hello world (r=bbouvier) (e49e7f4db2)
- Bug 1234985 - Fix js/src/asmjs build errors with unified builds disabled. r=luke (acb7954471)
- Bug 1231335 - part 1, runtime switch for SAB+Atomics. r=waldo (973eb7edc1)
- Bug 1231335 - part 2, asm.js runtime SAB+Atomics gating. r=luke (b40a8d0b3f)
- Bug 1225040 - generalize jsapi-tests for SAB. r=arai (29240f91e1)
- Bug 1232211 - Don't perform various SharedArrayBuffer-centric typed array tests in builds with SharedArrayBuffer disabled (i.e. Aurora at next uplift). rs=lth over IRC, responding to cassandra!philor's concerns (f3df4b0c17)
- Bug 1231335 - part 3, testing functions and test cases. r=waldo (b91910ea28)
- Bug 1231335 - part 4, js shell. r=waldo (4a96addbde)
- Bug 1234985 - Odin: move mutedError and displayURL into AsmJSModule (r=bbouvier) (6be0172a15)
- Bug 1229399: Remove expression statements opcodes in wasm; r=luke (a046188e98)
- Bug 1229399: Allow to peek into the internal wasm IR; r=luke (f4b0497d89)
- Bug 1234985 - Odin: make names optional and supplied at the end (r=b ouvier) (ee7864fe7e)
- Bug 1238679 - Rename s/SIMD/Simd/ in type and function names. r=bbouvier (1d6c277644)
- Bug 1238679 - Add enum class SIMDOperation. r=bbouvier (eb7ce840da)
- Bug 1187232 - Access TypedArray's [[ArrayLength]] instead of performing property access; r=Waldo (a66850e72f)
- Bug 1239068 - Inline "PossiblyTypedArrayLength" intrinsic; r=Waldo (1a39045055)
- Bug 1238679 - Put JSJitInfo::depth in anonymous union. r=nbp (64d6859ee2)
- Bug 1238679 - Provide per-operation JSJitInfo for the SIMD functions. r=bbouvier (cd242c1f5d)
- Backed out changeset 7bda46f81215 (bug 1225031) for spidermonkey test failures (7c7de9af43)
- Bug 1225031 - get rid of the AnyTypedArray abstraction (updated). r=waldo (a8e3593280)
- Bug 1237284: Make inlineSimd* functions take MIRType to avoid an indirection; r=jolesen (c046f5ac06)
- Bug 1238679 - Make inlineSimdLoad/Store take a MIRType argument. r=bbouvier (7aceb59fc1)
- Bug 1238679 - Implement main SIMD inlining dispatch. r=bbouvier (3b19775b14)
- Bug 1237445 - Use GCHashMap for VMWrapperMap, r=terrence (4a5b3a1575)
- Bug 1241454: Hoist SimdTypeDescr::Type into SimdType, unify AsmJSSimdType and SimdType; r=jolesen (6cf5f03517)
- Bug 1241454: Unify AsmJSSimdOperation and SimdOperation; r=jolesen (7506dec0e4)
- Bug 1241454: Change SIMD opcodes encoding in wasm; r=luke, r=jolesen (afa2b74cab)
- Bug 1242804 - Baldr: check enum limits (r=bbouvier) (1a68daa1c3)
- Bug 1242804 - Baldr: add local/get_local/set_local (r=bbouvier) (cc2448d2e9)
- Bug 1243239 - Baldr: tighten signature index check (r=bbouvier) (fd7081ab39)
- Bug 1243373: Ensure all declared functions are defined; r=luke (3c42295668)
- Bug 1243031: Throw when WebAssembly is not supported; r=luke (ade7cdc6a6)
- Bug 1242772 - Baldr: fix unterminated string literal (r=bbouvier) (273e26e669)
- Bug 1241886: Fix debug assertion if we're in dead code; r=luke (59c009191b)
- Bug 1234985 - Odin: use VarU32 for integer literals (r=bbouvier) (62c7292462)
- Bug 1234985 - Odin: stop requiring explicit final return (r=bbouvier) (2a8bf1639e)
- Bug 1242949: Implement Block in WebAssembly; r=luke (e8997f2a2e)
- Bug 1242949: Add explicit keyword to WasmAstBlock ctor; r=bustage (239cdcf5d4)
- Bug 1243626 - Baldr: tweak block text format (r=bbouvier) (a6fe9248ce)
- Bug 1243252 - Baldr: refactor exports (r=bbouvier) (49ca6f519f)
- Bug 1243632 - Baldr: check TypeError vs. SyntaxError (r=bbouvier) (c70ab8e8ec)
- Bug 1243252 - Baldr: add import section (r=bbouvier) (16b98660c8)
- Bug 1243633 - Baldr: add call/call_import (r=bbouvier) (63dd480e7d)
- Bug 1234985 - Hoist CStringHasher (r=sfink) (c019cff4b3)
- Bug 1237445 - Implement GCRekeyableHashMap, r=terrence (c4acd75ad0)
- Bug 1244272 - BaldrMonkey: Implement support for i32.add. r=luke (ede8918dfe)
- Bug 1244272 - BaldrMonkey: Implement the rest of the i32 binary operators. r=luke (bf11a38b86)
- Bug 1244272 - BaldrMonkey: Implement the f32 and 64 operators. r=luke (2ea37d6a95)
- Bug 1244403 - Baldr: move kind out of WasmAstNode and into WasmAstExpr (r=bbouvier) (498fd0b4a5)
- Silence a GCC warning in JitCompartment.h. No bug#, rs=jandem on IRC. (a23ccc42b3)
- Bug 1237445 - Use GCHashMap for ICStubCodeMap, r=terrence (b1d970f3c9)
- Bug 1244403 - Baldr: remove unnecessary 'end' argument from (r=bbouvier) (8da28c587c)
- Bug 1244403 - Baldr: expose export func entry offsets directly (r=bbouvier) (c6f82070fb)
- Bug 1234862 - Part 2: Always use DefaultGCPolicy with GCVector; r=sfink (6d8cf44216)
- Bug 1237445 - Use GCHashTables for ObjectGroup sweeping, r=terrence (7fd47cc6fb)
- Bug 1237445 - Use GCHashMap for (CCW) WrapperMap, r=terrence (cf6788b6a4)
- Bug 1237447 - Disable the crashreporter when crashing intentionally in TestPLDHash. r=njn (d4744f47d4)
- Bug 1237445 - Rekey ArrayObjectTable, r=terrence (2ccfca974c)
- Bug 1234862 - Part 3: Always use DefaultGCPolicy for GCHashTables; r=sfink (d0f2472c1b)
- Bug 1234862 - Part 4: Always use DefaultGCPolicy for TraceableFifo; r=sfink (069e134534)
- Bug 1239494 - Use GCHashMap to simplify nsWrapperCache; r=jonco (0bb0069fc4)
- Bug 1234862 - Part 5: Rename DefaultGCPolicy to GCPolicy; r=sfink (cb93f0c133)
- Bug 1234862 - Part 5.1: Followup comment fixes; r=sfink (bd2930ddf2)
- Fix recent non-unified build bustage (no bug, rs=sunfish) (6c40eba8bc)
- Bug 1243815: Put hard limits to some variable lengths in wasm; r=luke (1df12aa08e)
- Bug 1244403 - Baldr: factor out DynamicLinkData (r=bbouvier) (f257b901a3)
- Bug 1244403 - Baldr: put import section first (r=bbouvier) (47e726c907)
- Bug 1244571 - BaldrMonkey: Implement the unary operators. r=luke (e22132ccb1)
- Bug 1244571 - BaldrMonkey: Implement the comparison operators. r=luke (aa6e7a71cd)
- Bug 1244571 - BaldrMonkey: Implement parsing, encoding, and decoding for the conversion operators. r=luke (73d65dc416)
- Bug 1244571 - BaldrMonkey: Type-check the unary and binary operators. r=luke (4d7d41d49b)
- Bug 1244571 - BaldrMonkey: Implement parsing, encoding, and decoding for i64 operators. r=luke (c42a9b5496)
- Bug 1244571 - BaldrMonkey: Improve the readability of the parser code. r=luke (298c9c57a5)
- Bug 1244571 - BaldrMonkey: Fail decoding for operators which are not yet implemented r=luke (fdd4deff21)
- Bug 1245250 - BaldrMonkey: Refacfor min/max to make variadicity AsmJS-specific r=luke (f30e0471b9)
- Bug 1242342: Add If and IfElse to WebAssembly; r=luke (653223fdd3)
- Bug 1247755 - Baldr: disallow duplicate signature table entries (r=sunfish) (419fd0be57)
- Bug 1234397 - dispatch on the correct value. r=luke (4c7d5f5105)
- Bug 1233863 - ARM64: Allow test to pass with --no-asmjs, and when no JIT exists. r=luke (b89522c52c)
- Bug 1244405 - Odin: switch to dynamic page size, move heap constants to Wasm (r=bbouvier) (31d4b684b7)
- Bug 1234038 - Self-host ArrayBuffer.prototype.slice. r=lth (ab81205e4a)
- Bug 1121937 - Implement %TypedArray%.prototype.sort; r=jorendorff (aef7bb1228)
- Bug 1101256 - Implement detachment checks for %TypedArray% methods (patch 1 of 2); r=Waldo (cb13a5078b)
- Bug 1101256 - Implement detachment checks for %TypedArray% methods (patch 2 of 2); r=Waldo (b429b87a3d)
- Bug 715181 - Self-host Array.sort; r=till (f87f85634c)
- Bug 1079844 - Adjust TypedArray.js comments to refer to detachment rather than neutering. r=mrrrgn (ac2b366958)
- Bug 1079844 - Rename scattered bits of 'neuter' terminology to detachment terminology. r=jandem (ae20a9677c)
- Bug 1079844 - Rename JS_ARRAYBUFFER_NEUTERED_FLAG to use detachment terminology. r=sfink (670281d8b0)
- Bug 1079844 - Rename isNeutered() to isDetached(), and rename isNeutered() on views to hasDetachedBuffer(). r=sfink (b5595746aa)
- Bug 1079844 - Convert the JS_NeuterArrayBufferObject API to detachment terminology. r=till (2d176589bd)
- Bug 1079844 - Rename JS_IsNeuteredArrayBufferObject to JS_IsDetachedArrayBufferObject. r=till (9607cf673d)
- Bug 1079844 - Refer to "detaching" instead of "neutering" of ArrayBuffers, in JIT optimization tracking code. r=jandem (a4a17233d0)
- Bug 1079844 - Rename TI's ObjectKey flag to use detachment terminology. r=jandem (8865017109)
- Bug 1079844 - Rename CheckForNeuteredTypedObject and the flag underlying it to use detachment terminology. r=jandem (67ace7669e)
- Bug 1079844 - Rename ABO::neuter to ABO::detach. r=sfink (37c71de755)
- Bug 1079844 - Rename ArrayBufferViewObject::neuter to ABVO::notifyBufferDetached. Detachment applies only to ArrayBuffers, and the methods didn't actually neuter anything, so "notify" makes more sense as the verb here. r=sfink (2ebf7779a7)
- Bug 1079844 - Rename ABO::neuterView to a standalone NoteViewBufferWasDetached function. r=sfink (3ce2977123)
- Bug 1244405 - Odin: refactor ArrayBufferObject::createForWasm out of prepareForAsmJS (r=bbouvier) (3f5ed523f0)
- Bug 1243633 - Odin: tidy up call emitting (r=bbouvier) (89256a6d57)
- Bug 1243633 - Odin: switch to lineOrBytecode from line/column (r=bbouvier) (ded5ff23de)
- Bug 1242342: Set return type accordingly to the compilation mode; r=luke (f4cdff1644)
- Bug 1244405 - Baldr: fix wasm function export name (r=bbouvier) (c6ea5f8194)
- Bug 1244405 - Baldr: add memory section (r=bbouvier) (c98fc66e50)
- Bug 1243252 - Remove const restriction on CharsToNewUTF8CharsZ (r=jandem) (9ef020aff6)
- fix neutered/detached (2a675ae62e)
- Bug 1242342: Replace Ternary by IfElse which return expressions; r=luke (aa1535de89)
- Bug 1244405 - Baldr: add memory exports (r=bbouvier) (9a495599df)
- Bug 1240583 - Odin: remove the sync interrupt stub (r=bbouvier) (a3811b0cc5)
- Bug 1242043 - {Array,%TypedArray%}.prototype.{i,lastI}ndexOf should never return -0. r=jorendorff (485f5d62dc)
- Bug 1180290 - Part 1: Add prefix parameter to IdToFunctionName. r=till (e33ed94f64)
- Bug 1180290 - Part 2: Handle prefix in DefinePropertyById. r=till,smaug (5c3333ef68)
- Bug 1180290 - Part 3: Use canonical name in native getter. r=till (213b6d1b7f)
- Bug 1180290 - Part 4: Add tests for builtin getter name. r=till (005f1e4da7)
- Bug 1180290 - Part 5: Remove getter/setter variant for ThrowInvalidThis message. r=bz (fa8224af50)
- Bug 1236548 - Allow JS_UNINITIALIZED_LEXICAL in jit::InvokeFunction. (r=Waldo) (33b1f88506)
- Bug 1212533 - Change the out-param of js/JS::Construct from MutableHandleValue to MutableHandleObject. r=arai, r=jorendorff (7910b24126)
- Bug 1233857 - Teach the JIT how to put individual elements' edges in the store buffer; r=jandem (7d0b35b028)
- Bug 1236548 - Followup: Make added test actually pass. (rs=Waldo) (0f1e6861b1)
- Bug 1216166 - Unify CustomEvent.cpp with the rest of the unified sources, r=smaug (eacd905246)
- Bug 1233831 - Part 0: JSAPI plumbing for nursery collection callbacks; r=terrence (1417ff456a)
- Bug 1233831 - Part 1: Call the callback on nursery collections; r=terrence (cf720fc8c9)
- Bug 1233831 - Part 2: Expose JS::gcreason::ExplainReason to embedders; r=terrence (5eaceb2a58)
- Bug 1233831 - Part 3: Install a callback to trace nursery collections; r=mccr8 (13bc8d19ff)
- Bug 1233831 - Part 4: Add metadata for the minor GC markers; r=vporof (cb42e1e42a)
- Bug 1233831 - Part 5: Test the minor GC markers; r=vporof (5f8ae59242)
- Bug 1218468 - Remove worker markers on each check in browser_timelineMarkers-02.js, r=me (33efc054c0)
- Bug 1233831 - Follow up: Expand sanitizeMarkers to filter out minor GC markers; r=jsantell (b99fb1dbbc)
- Bug 1239314 - Make marking validation a normal zeal mode; r=jonco (4668054d11)
- Bug 1233857 - Follow up: Add a new GC zeal mode for the elements edges barrier; r=terrence (61a7db4357)
- Bug 1245767 - Allow combining different gczeal modes. r=terrence (8d90fd103e)
- Bug 1242262 - Remove the last vestiges of JS_GC_MARKING_VALIDATION (it's a GC Zeal mode now). r=terrence (3ca086aed4)
- Bug 1249795 - Clear strong refs to message managers once we've destroyed nsFrameLoader, r=billm (b562395eb9)
- Bug 1244405 - Baldr: add memory segments (r=sunfish) (c7850ee495)
- Bug 1246433 - BaldrMonkey: Implement integer and float literals. r=luke (d668caa1bd)
- Bug 1240583 - Odin: refactor x86/x64 loads/stores (r=sunfish) (df85bc0aa3)
- Bug 1240583 - Odin: replace retargetWithOffset (r=bbouvier) (627815cb6d)
- Bug 757969 - use __thread in ThreadLocal; r=froydnj (5e06f7ef9e)
- bug 1244128 - stop exporting js friend api symbols from libxul r=glandium (894792316d)
- Bug 912337 - Make a new Error subclass: Debugger.DebuggeeWouldRun. (r=jimb) (a020dfd40c)
- Bug 1248101 - Make dtoa_malloc infallible. r=sfink. (0828c1b15b)
- Bug 1245737 (part 1) - Enable |Omit_Private_Memory| for our dtoa implementation. r=bhackett. (aaa0ebad66)
- Bug 1245737 (part 0) - Fix a leak in DtoaState. r=sfink. (3007242e86)
- Bug 1245737 (part 2) - Don't bother measuring DtoaState. r=bhackett. (db33501ddd)
- Bug 1243888 - Derive RootKind automatically from TraceKind; r=sfink (08421e1c6c)
- Bug 1244358 - Support all types in PersistentRooted; r=sfink (bc1a8ada00)
- Bug 1248233. Make js::ReportOutOfMemory respect the autoJSAPIOwnsErrorReporting() flag on the context options. r=luke (5ee05b61e6)
- Bug 1235151 - Factor all the install.rdfs used for langpacks. r=mshal (0e10679502)
- Bug 1235151 - Use #include in toolkit/locales/generic/install.rdf instead of -I on the preprocessor command line. r=mshal (31fdfea3de)
- Bug 1227388 - Finish removing dehydra support. r=mshal (aa43dfce95)
- Bug 1205814 - mozilla eslint plug: allow top level var only r=pbrosset (f247252d4b)
- Bug 1218409 - Eslint rule that checks for balanced listeners. r=miker (45d42a1014)
- Bug 1205814 - Missed a brief description in rst docs r=pbrosset (13fb24c5c4)
- Bug 1218425 - ESLint rule that warns against aArg notation in function params; r=miker (e811f2c8f7)
- Bug 1217851 - Fix mozplugin's import-headjs-globals on Windows. r=pbro (685f450fcb)
- Bug 1218412 - Create ESLint rule to check for CPOWS in browser mochitests r=pbrosset (c505ab4ed3)
- Bug 1217922 - eslint head.js plugin does not seem to work r=pbrosset (8d3782057e)
- Bug 1224735 - don't use "for..of" in eslint plugin. r=miker (90ecebec24)
- Bug 1225289 - Make eslint plugin code conform to .eslintrc r=pbrosset (007bf4c6b8)
- Bug 1222232 - Help mach eslint find espree and escope r=pbrosset (d0f7c5987b)
- Bug 1229858: Add a preprocessor to convert XBL into JavaScript blocks for eslint. r=miker (99bd8d62c4)
- Bug 1230300: Fix mach eslint to pass command arguments through to eslint. r=gps (7b54b30a8f)
- Bug 1231720 - Log when ESLint finishes running. r=nalexander (12705e260d)
- Bug 1234164 - Fix Ion GETNAME stubs to check for uninitialized lexicals. (r=efaust) (561d6e2d1c)
- Bug 1239519 - Fix debug-only initializion order issue detected by ASan. r=luke (8aa8b63fb6)
- Bug 1242840 - Drop profiler invalidation event if we hit OOM and make invalidation infallible r=jandem (8dfe4d99a5)
- Bug 1232386 - Fix NativeObject::growSlotsStatic to recover from OOM. r=terrence (284f47d6b1)
- Bug 1235032 - check value of obj, remake while () {} to do { } while(). r=jorendorff (0cb646a76e)
- Bug 1228327 - Fix bogus assert in GenerateDenseElementHole, add some comments. r=bhackett (e7fc0db6a0)
- Bug 1232859 - Don't crash or abort compilation if we're unable to compile the regex stubs. r=nbp (0ec48d44b5)
- Bug 1237566 - Bake in global this-value in IonBuilder::jsop_functionthis in some cases. r=h4writer (bd1fb0e8d8)
- Bug 1000780 - Part 7: Fix perf regressions introduced in part 1. r=jandem (629452aa16)
- Bug 1240100: IonMonkey: Magic values cannot flow through an instruction, r=jandem (f644caf377)
- Bug 1244098 - Attempt to fold JSOP_IN to false in IonBuilder based on TI. r=bhackett (e964e1d2bf)
- Bug 1238592 - IonMonkey: Optimize away any OSR fixup blocks that are ultimately unreachable. r=nbp (3e7ad0d61a)
- Bug 1245171 - Ensure enough ballast space in js::jit::AddKeepAliveInstructions. r=jandem (ba48c30163)
- Bug 1244828 - Ensure enough ballast space in TypeAnalyzer::adjustPhiInputs. r=h4writer (126c899284)
- Bug 1244828 - Ensure enough ballast space in TypeAnalyzer::adjustInputs. r=h4writer (0bf3d584b9)
- Bug 1112537 - Optimize String#split('foo').join('bar') pattern. r=nbp (75fb0e4124)
1494 lines
46 KiB
C++
1494 lines
46 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// We're dividing JS objects into 3 categories:
|
|
//
|
|
// 1. "real" roots, held by the JS engine itself or rooted through the root
|
|
// and lock JS APIs. Roots from this category are considered black in the
|
|
// cycle collector, any cycle they participate in is uncollectable.
|
|
//
|
|
// 2. certain roots held by C++ objects that are guaranteed to be alive.
|
|
// Roots from this category are considered black in the cycle collector,
|
|
// and any cycle they participate in is uncollectable. These roots are
|
|
// traced from TraceNativeBlackRoots.
|
|
//
|
|
// 3. all other roots held by C++ objects that participate in cycle
|
|
// collection, held by us (see TraceNativeGrayRoots). Roots from this
|
|
// category are considered grey in the cycle collector; whether or not
|
|
// they are collected depends on the objects that hold them.
|
|
//
|
|
// Note that if a root is in multiple categories the fact that it is in
|
|
// category 1 or 2 that takes precedence, so it will be considered black.
|
|
//
|
|
// During garbage collection we switch to an additional mark color (gray)
|
|
// when tracing inside TraceNativeGrayRoots. This allows us to walk those
|
|
// roots later on and add all objects reachable only from them to the
|
|
// cycle collector.
|
|
//
|
|
// Phases:
|
|
//
|
|
// 1. marking of the roots in category 1 by having the JS GC do its marking
|
|
// 2. marking of the roots in category 2 by having the JS GC call us back
|
|
// (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots
|
|
// 3. marking of the roots in category 3 by TraceNativeGrayRoots using an
|
|
// additional color (gray).
|
|
// 4. end of GC, GC can sweep its heap
|
|
//
|
|
// At some later point, when the cycle collector runs:
|
|
//
|
|
// 5. walk gray objects and add them to the cycle collector, cycle collect
|
|
//
|
|
// JS objects that are part of cycles the cycle collector breaks will be
|
|
// collected by the next JS GC.
|
|
//
|
|
// If WantAllTraces() is false the cycle collector will not traverse roots
|
|
// from category 1 or any JS objects held by them. Any JS objects they hold
|
|
// will already be marked by the JS GC and will thus be colored black
|
|
// themselves. Any C++ objects they hold will have a missing (untraversed)
|
|
// edge from the JS object to the C++ object and so it will be marked black
|
|
// too. This decreases the number of objects that the cycle collector has to
|
|
// deal with.
|
|
// To improve debugging, if WantAllTraces() is true all JS objects are
|
|
// traversed.
|
|
|
|
#include "mozilla/CycleCollectedJSRuntime.h"
|
|
#include <algorithm>
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/AutoRestore.h"
|
|
#include "mozilla/Move.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "mozilla/Snprintf.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/TimelineConsumers.h"
|
|
#include "mozilla/TimelineMarker.h"
|
|
#include "mozilla/DebuggerOnGCRunnable.h"
|
|
#include "mozilla/dom/DOMJSClass.h"
|
|
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/ScriptSettings.h"
|
|
#include "jsprf.h"
|
|
#include "js/Debug.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsCycleCollectionNoteRootCallback.h"
|
|
#include "nsCycleCollectionParticipant.h"
|
|
#include "nsCycleCollector.h"
|
|
#include "nsDOMJSUtils.h"
|
|
#include "nsJSUtils.h"
|
|
#include "nsWrapperCache.h"
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
#include "nsExceptionHandler.h"
|
|
#endif
|
|
|
|
#include "nsIException.h"
|
|
#include "nsThread.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "xpcpublic.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
namespace mozilla {
|
|
|
|
struct DeferredFinalizeFunctionHolder
|
|
{
|
|
DeferredFinalizeFunction run;
|
|
void* data;
|
|
};
|
|
|
|
class IncrementalFinalizeRunnable : public nsRunnable
|
|
{
|
|
typedef nsAutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
|
|
typedef CycleCollectedJSRuntime::DeferredFinalizerTable DeferredFinalizerTable;
|
|
|
|
CycleCollectedJSRuntime* mRuntime;
|
|
DeferredFinalizeArray mDeferredFinalizeFunctions;
|
|
uint32_t mFinalizeFunctionToRun;
|
|
bool mReleasing;
|
|
|
|
static const PRTime SliceMillis = 5; /* ms */
|
|
|
|
public:
|
|
IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
|
|
DeferredFinalizerTable& aFinalizerTable);
|
|
virtual ~IncrementalFinalizeRunnable();
|
|
|
|
void ReleaseNow(bool aLimited);
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
struct NoteWeakMapChildrenTracer : public JS::CallbackTracer
|
|
{
|
|
NoteWeakMapChildrenTracer(JSRuntime* aRt,
|
|
nsCycleCollectionNoteRootCallback& aCb)
|
|
: JS::CallbackTracer(aRt), mCb(aCb), mTracedAny(false), mMap(nullptr),
|
|
mKey(nullptr), mKeyDelegate(nullptr)
|
|
{
|
|
}
|
|
void onChild(const JS::GCCellPtr& aThing) override;
|
|
nsCycleCollectionNoteRootCallback& mCb;
|
|
bool mTracedAny;
|
|
JSObject* mMap;
|
|
JS::GCCellPtr mKey;
|
|
JSObject* mKeyDelegate;
|
|
};
|
|
|
|
void
|
|
NoteWeakMapChildrenTracer::onChild(const JS::GCCellPtr& aThing)
|
|
{
|
|
if (aThing.is<JSString>()) {
|
|
return;
|
|
}
|
|
|
|
if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
|
|
return;
|
|
}
|
|
|
|
if (AddToCCKind(aThing.kind())) {
|
|
mCb.NoteWeakMapping(mMap, mKey, mKeyDelegate, aThing);
|
|
mTracedAny = true;
|
|
} else {
|
|
JS::TraceChildren(this, aThing);
|
|
}
|
|
}
|
|
|
|
struct NoteWeakMapsTracer : public js::WeakMapTracer
|
|
{
|
|
NoteWeakMapsTracer(JSRuntime* aRt, nsCycleCollectionNoteRootCallback& aCccb)
|
|
: js::WeakMapTracer(aRt), mCb(aCccb), mChildTracer(aRt, aCccb)
|
|
{
|
|
}
|
|
void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override;
|
|
nsCycleCollectionNoteRootCallback& mCb;
|
|
NoteWeakMapChildrenTracer mChildTracer;
|
|
};
|
|
|
|
void
|
|
NoteWeakMapsTracer::trace(JSObject* aMap, JS::GCCellPtr aKey,
|
|
JS::GCCellPtr aValue)
|
|
{
|
|
// If nothing that could be held alive by this entry is marked gray, return.
|
|
if ((!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
|
|
MOZ_LIKELY(!mCb.WantAllTraces())) {
|
|
if (!aValue || !JS::GCThingIsMarkedGray(aValue) || aValue.is<JSString>()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// The cycle collector can only properly reason about weak maps if it can
|
|
// reason about the liveness of their keys, which in turn requires that
|
|
// the key can be represented in the cycle collector graph. All existing
|
|
// uses of weak maps use either objects or scripts as keys, which are okay.
|
|
MOZ_ASSERT(AddToCCKind(aKey.kind()));
|
|
|
|
// As an emergency fallback for non-debug builds, if the key is not
|
|
// representable in the cycle collector graph, we treat it as marked. This
|
|
// can cause leaks, but is preferable to ignoring the binding, which could
|
|
// cause the cycle collector to free live objects.
|
|
if (!AddToCCKind(aKey.kind())) {
|
|
aKey = nullptr;
|
|
}
|
|
|
|
JSObject* kdelegate = nullptr;
|
|
if (aKey.is<JSObject>()) {
|
|
kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
|
|
}
|
|
|
|
if (AddToCCKind(aValue.kind())) {
|
|
mCb.NoteWeakMapping(aMap, aKey, kdelegate, aValue);
|
|
} else {
|
|
mChildTracer.mTracedAny = false;
|
|
mChildTracer.mMap = aMap;
|
|
mChildTracer.mKey = aKey;
|
|
mChildTracer.mKeyDelegate = kdelegate;
|
|
|
|
if (aValue.is<JSString>()) {
|
|
JS::TraceChildren(&mChildTracer, aValue);
|
|
}
|
|
|
|
// The delegate could hold alive the key, so report something to the CC
|
|
// if we haven't already.
|
|
if (!mChildTracer.mTracedAny &&
|
|
aKey && JS::GCThingIsMarkedGray(aKey) && kdelegate) {
|
|
mCb.NoteWeakMapping(aMap, aKey, kdelegate, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is based on the logic in FixWeakMappingGrayBitsTracer::trace.
|
|
struct FixWeakMappingGrayBitsTracer : public js::WeakMapTracer
|
|
{
|
|
explicit FixWeakMappingGrayBitsTracer(JSRuntime* aRt)
|
|
: js::WeakMapTracer(aRt)
|
|
{
|
|
}
|
|
|
|
void
|
|
FixAll()
|
|
{
|
|
do {
|
|
mAnyMarked = false;
|
|
js::TraceWeakMaps(this);
|
|
} while (mAnyMarked);
|
|
}
|
|
|
|
void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override
|
|
{
|
|
// If nothing that could be held alive by this entry is marked gray, return.
|
|
bool delegateMightNeedMarking = aKey && JS::GCThingIsMarkedGray(aKey);
|
|
bool valueMightNeedMarking = aValue && JS::GCThingIsMarkedGray(aValue) &&
|
|
aValue.kind() != JS::TraceKind::String;
|
|
if (!delegateMightNeedMarking && !valueMightNeedMarking) {
|
|
return;
|
|
}
|
|
|
|
if (!AddToCCKind(aKey.kind())) {
|
|
aKey = nullptr;
|
|
}
|
|
|
|
if (delegateMightNeedMarking && aKey.is<JSObject>()) {
|
|
JSObject* kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
|
|
if (kdelegate && !JS::ObjectIsMarkedGray(kdelegate)) {
|
|
if (JS::UnmarkGrayGCThingRecursively(aKey)) {
|
|
mAnyMarked = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aValue && JS::GCThingIsMarkedGray(aValue) &&
|
|
(!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
|
|
(!aMap || !JS::ObjectIsMarkedGray(aMap)) &&
|
|
aValue.kind() != JS::TraceKind::Shape) {
|
|
if (JS::UnmarkGrayGCThingRecursively(aValue)) {
|
|
mAnyMarked = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool mAnyMarked;
|
|
};
|
|
|
|
static void
|
|
CheckParticipatesInCycleCollection(JS::GCCellPtr aThing, const char* aName,
|
|
void* aClosure)
|
|
{
|
|
bool* cycleCollectionEnabled = static_cast<bool*>(aClosure);
|
|
|
|
if (*cycleCollectionEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (AddToCCKind(aThing.kind()) && JS::GCThingIsMarkedGray(aThing)) {
|
|
*cycleCollectionEnabled = true;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
JSGCThingParticipant::Traverse(void* aPtr,
|
|
nsCycleCollectionTraversalCallback& aCb)
|
|
{
|
|
auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>(
|
|
reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime,
|
|
mGCThingCycleCollectorGlobal));
|
|
|
|
JS::GCCellPtr cellPtr(aPtr, js::GCThingTraceKind(aPtr));
|
|
runtime->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL, cellPtr, aCb);
|
|
return NS_OK;
|
|
}
|
|
|
|
// NB: This is only used to initialize the participant in
|
|
// CycleCollectedJSRuntime. It should never be used directly.
|
|
static JSGCThingParticipant sGCThingCycleCollectorGlobal;
|
|
|
|
NS_IMETHODIMP
|
|
JSZoneParticipant::Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
|
|
{
|
|
auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>(
|
|
reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime,
|
|
mJSZoneCycleCollectorGlobal));
|
|
|
|
MOZ_ASSERT(!aCb.WantAllTraces());
|
|
JS::Zone* zone = static_cast<JS::Zone*>(aPtr);
|
|
|
|
runtime->TraverseZone(zone, aCb);
|
|
return NS_OK;
|
|
}
|
|
|
|
struct TraversalTracer : public JS::CallbackTracer
|
|
{
|
|
TraversalTracer(JSRuntime* aRt, nsCycleCollectionTraversalCallback& aCb)
|
|
: JS::CallbackTracer(aRt, DoNotTraceWeakMaps), mCb(aCb)
|
|
{
|
|
}
|
|
void onChild(const JS::GCCellPtr& aThing) override;
|
|
nsCycleCollectionTraversalCallback& mCb;
|
|
};
|
|
|
|
void
|
|
TraversalTracer::onChild(const JS::GCCellPtr& aThing)
|
|
{
|
|
// Don't traverse non-gray objects, unless we want all traces.
|
|
if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This function needs to be careful to avoid stack overflow. Normally, when
|
|
* AddToCCKind is true, the recursion terminates immediately as we just add
|
|
* |thing| to the CC graph. So overflow is only possible when there are long
|
|
* or cyclic chains of non-AddToCCKind GC things. Places where this can occur
|
|
* use special APIs to handle such chains iteratively.
|
|
*/
|
|
if (AddToCCKind(aThing.kind())) {
|
|
if (MOZ_UNLIKELY(mCb.WantDebugInfo())) {
|
|
char buffer[200];
|
|
getTracingEdgeName(buffer, sizeof(buffer));
|
|
mCb.NoteNextEdgeName(buffer);
|
|
}
|
|
if (aThing.is<JSObject>()) {
|
|
mCb.NoteJSObject(&aThing.as<JSObject>());
|
|
} else {
|
|
mCb.NoteJSScript(&aThing.as<JSScript>());
|
|
}
|
|
} else if (aThing.is<js::Shape>()) {
|
|
// The maximum depth of traversal when tracing a Shape is unbounded, due to
|
|
// the parent pointers on the shape.
|
|
JS_TraceShapeCycleCollectorChildren(this, aThing);
|
|
} else if (aThing.is<js::ObjectGroup>()) {
|
|
// The maximum depth of traversal when tracing an ObjectGroup is unbounded,
|
|
// due to information attached to the groups which can lead other groups to
|
|
// be traced.
|
|
JS_TraceObjectGroupCycleCollectorChildren(this, aThing);
|
|
} else if (!aThing.is<JSString>()) {
|
|
JS::TraceChildren(this, aThing);
|
|
}
|
|
}
|
|
|
|
static void
|
|
NoteJSChildGrayWrapperShim(void* aData, JS::GCCellPtr aThing)
|
|
{
|
|
TraversalTracer* trc = static_cast<TraversalTracer*>(aData);
|
|
trc->onChild(aThing);
|
|
}
|
|
|
|
/*
|
|
* The cycle collection participant for a Zone is intended to produce the same
|
|
* results as if all of the gray GCthings in a zone were merged into a single node,
|
|
* except for self-edges. This avoids the overhead of representing all of the GCthings in
|
|
* the zone in the cycle collector graph, which should be much faster if many of
|
|
* the GCthings in the zone are gray.
|
|
*
|
|
* Zone merging should not always be used, because it is a conservative
|
|
* approximation of the true cycle collector graph that can incorrectly identify some
|
|
* garbage objects as being live. For instance, consider two cycles that pass through a
|
|
* zone, where one is garbage and the other is live. If we merge the entire
|
|
* zone, the cycle collector will think that both are alive.
|
|
*
|
|
* We don't have to worry about losing track of a garbage cycle, because any such garbage
|
|
* cycle incorrectly identified as live must contain at least one C++ to JS edge, and
|
|
* XPConnect will always add the C++ object to the CC graph. (This is in contrast to pure
|
|
* C++ garbage cycles, which must always be properly identified, because we clear the
|
|
* purple buffer during every CC, which may contain the last reference to a garbage
|
|
* cycle.)
|
|
*/
|
|
|
|
// NB: This is only used to initialize the participant in
|
|
// CycleCollectedJSRuntime. It should never be used directly.
|
|
static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
|
|
|
|
static
|
|
void JSObjectsTenuredCb(JSRuntime* aRuntime, void* aData)
|
|
{
|
|
static_cast<CycleCollectedJSRuntime*>(aData)->JSObjectsTenured(aRuntime);
|
|
}
|
|
|
|
CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
|
|
uint32_t aMaxBytes,
|
|
uint32_t aMaxNurseryBytes)
|
|
: mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal)
|
|
, mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
|
|
, mJSRuntime(nullptr)
|
|
, mPrevGCSliceCallback(nullptr)
|
|
, mPrevGCNurseryCollectionCallback(nullptr)
|
|
, mJSHolders(256)
|
|
, mDoingStableStates(false)
|
|
, mOutOfMemoryState(OOMState::OK)
|
|
, mLargeAllocationFailureState(OOMState::OK)
|
|
{
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
mOwningThread = thread.forget().downcast<nsThread>().take();
|
|
MOZ_RELEASE_ASSERT(mOwningThread);
|
|
|
|
mOwningThread->SetScriptObserver(this);
|
|
// The main thread has a base recursion depth of 0, workers of 1.
|
|
mBaseRecursionDepth = RecursionDepth();
|
|
|
|
mozilla::dom::InitScriptSettings();
|
|
|
|
mJSRuntime = JS_NewRuntime(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
|
|
if (!mJSRuntime) {
|
|
MOZ_CRASH();
|
|
}
|
|
|
|
if (!JS_AddExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this)) {
|
|
MOZ_CRASH();
|
|
}
|
|
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
|
|
JS_SetGCCallback(mJSRuntime, GCCallback, this);
|
|
mPrevGCSliceCallback = JS::SetGCSliceCallback(mJSRuntime, GCSliceCallback);
|
|
|
|
if (NS_IsMainThread()) {
|
|
// We would like to support all threads here, but the way timeline consumers
|
|
// are set up currently, you can either add a marker for one specific
|
|
// docshell, or for every consumer globally. We would like to add a marker
|
|
// for every consumer observing anything on this thread, but that is not
|
|
// currently possible. For now, add global markers only when we are on the
|
|
// main thread, since the UI for this tracing data only displays data
|
|
// relevant to the main-thread.
|
|
mPrevGCNurseryCollectionCallback = JS::SetGCNurseryCollectionCallback(
|
|
mJSRuntime, GCNurseryCollectionCallback);
|
|
}
|
|
|
|
JS_SetObjectsTenuredCallback(mJSRuntime, JSObjectsTenuredCb, this);
|
|
JS::SetOutOfMemoryCallback(mJSRuntime, OutOfMemoryCallback, this);
|
|
JS::SetLargeAllocationFailureCallback(mJSRuntime,
|
|
LargeAllocationFailureCallback, this);
|
|
JS_SetContextCallback(mJSRuntime, ContextCallback, this);
|
|
JS_SetDestroyZoneCallback(mJSRuntime, XPCStringConvert::FreeZoneCache);
|
|
JS_SetSweepZoneCallback(mJSRuntime, XPCStringConvert::ClearZoneCache);
|
|
|
|
static js::DOMCallbacks DOMcallbacks = {
|
|
InstanceClassHasProtoAtDepth
|
|
};
|
|
SetDOMCallbacks(mJSRuntime, &DOMcallbacks);
|
|
|
|
JS::dbg::SetDebuggerMallocSizeOf(mJSRuntime, moz_malloc_size_of);
|
|
|
|
nsCycleCollector_registerJSRuntime(this);
|
|
}
|
|
|
|
CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
|
|
{
|
|
MOZ_ASSERT(mJSRuntime);
|
|
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
|
|
|
|
// Last chance to process any events.
|
|
ProcessMetastableStateQueue(mBaseRecursionDepth);
|
|
MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
|
|
|
|
ProcessStableStateQueue();
|
|
MOZ_ASSERT(mStableStateEvents.IsEmpty());
|
|
|
|
// Clear mPendingException first, since it might be cycle collected.
|
|
mPendingException = nullptr;
|
|
|
|
JS_DestroyRuntime(mJSRuntime);
|
|
mJSRuntime = nullptr;
|
|
nsCycleCollector_forgetJSRuntime();
|
|
|
|
mozilla::dom::DestroyScriptSettings();
|
|
|
|
mOwningThread->SetScriptObserver(nullptr);
|
|
NS_RELEASE(mOwningThread);
|
|
}
|
|
|
|
size_t
|
|
CycleCollectedJSRuntime::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
size_t n = 0;
|
|
|
|
// We're deliberately not measuring anything hanging off the entries in
|
|
// mJSHolders.
|
|
n += mJSHolders.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
return n;
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::UnmarkSkippableJSHolders()
|
|
{
|
|
for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
|
|
void* holder = iter.Key();
|
|
nsScriptObjectTracer*& tracer = iter.Data();
|
|
tracer->CanSkip(holder, true);
|
|
}
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
|
|
nsCycleCollectionTraversalCallback& aCb) const
|
|
{
|
|
if (!aCb.WantDebugInfo()) {
|
|
aCb.DescribeGCedNode(aIsMarked, "JS Object");
|
|
return;
|
|
}
|
|
|
|
char name[72];
|
|
uint64_t compartmentAddress = 0;
|
|
if (aThing.is<JSObject>()) {
|
|
JSObject* obj = &aThing.as<JSObject>();
|
|
compartmentAddress = (uint64_t)js::GetObjectCompartment(obj);
|
|
const js::Class* clasp = js::GetObjectClass(obj);
|
|
|
|
// Give the subclass a chance to do something
|
|
if (DescribeCustomObjects(obj, clasp, name)) {
|
|
// Nothing else to do!
|
|
} else if (js::IsFunctionObject(obj)) {
|
|
JSFunction* fun = JS_GetObjectFunction(obj);
|
|
JSString* str = JS_GetFunctionDisplayId(fun);
|
|
if (str) {
|
|
JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(str);
|
|
nsAutoString chars;
|
|
AssignJSFlatString(chars, flat);
|
|
NS_ConvertUTF16toUTF8 fname(chars);
|
|
snprintf_literal(name, "JS Object (Function - %s)", fname.get());
|
|
} else {
|
|
snprintf_literal(name, "JS Object (Function)");
|
|
}
|
|
} else {
|
|
snprintf_literal(name, "JS Object (%s)", clasp->name);
|
|
}
|
|
} else {
|
|
snprintf_literal(name, "JS %s", JS::GCTraceKindToAscii(aThing.kind()));
|
|
}
|
|
|
|
// Disable printing global for objects while we figure out ObjShrink fallout.
|
|
aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::NoteGCThingJSChildren(JS::GCCellPtr aThing,
|
|
nsCycleCollectionTraversalCallback& aCb) const
|
|
{
|
|
MOZ_ASSERT(mJSRuntime);
|
|
TraversalTracer trc(mJSRuntime, aCb);
|
|
JS::TraceChildren(&trc, aThing);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp,
|
|
JSObject* aObj,
|
|
nsCycleCollectionTraversalCallback& aCb) const
|
|
{
|
|
MOZ_ASSERT(aClasp);
|
|
MOZ_ASSERT(aClasp == js::GetObjectClass(aObj));
|
|
|
|
if (NoteCustomGCThingXPCOMChildren(aClasp, aObj, aCb)) {
|
|
// Nothing else to do!
|
|
return;
|
|
}
|
|
// XXX This test does seem fragile, we should probably whitelist classes
|
|
// that do hold a strong reference, but that might not be possible.
|
|
else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
|
|
aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
|
|
aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
|
|
} else {
|
|
const DOMJSClass* domClass = GetDOMClass(aObj);
|
|
if (domClass) {
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
|
|
// It's possible that our object is an unforgeable holder object, in
|
|
// which case it doesn't actually have a C++ DOM object associated with
|
|
// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
|
|
// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
|
|
if (domClass->mDOMObjectIsISupports) {
|
|
aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
|
|
} else if (domClass->mParticipant) {
|
|
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
|
|
domClass->mParticipant);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
|
|
nsCycleCollectionTraversalCallback& aCb)
|
|
{
|
|
bool isMarkedGray = JS::GCThingIsMarkedGray(aThing);
|
|
|
|
if (aTs == TRAVERSE_FULL) {
|
|
DescribeGCThing(!isMarkedGray, aThing, aCb);
|
|
}
|
|
|
|
// If this object is alive, then all of its children are alive. For JS objects,
|
|
// the black-gray invariant ensures the children are also marked black. For C++
|
|
// objects, the ref count from this object will keep them alive. Thus we don't
|
|
// need to trace our children, unless we are debugging using WantAllTraces.
|
|
if (!isMarkedGray && !aCb.WantAllTraces()) {
|
|
return;
|
|
}
|
|
|
|
if (aTs == TRAVERSE_FULL) {
|
|
NoteGCThingJSChildren(aThing, aCb);
|
|
}
|
|
|
|
if (aThing.is<JSObject>()) {
|
|
JSObject* obj = &aThing.as<JSObject>();
|
|
NoteGCThingXPCOMChildren(js::GetObjectClass(obj), obj, aCb);
|
|
}
|
|
}
|
|
|
|
struct TraverseObjectShimClosure
|
|
{
|
|
nsCycleCollectionTraversalCallback& cb;
|
|
CycleCollectedJSRuntime* self;
|
|
};
|
|
|
|
void
|
|
CycleCollectedJSRuntime::TraverseZone(JS::Zone* aZone,
|
|
nsCycleCollectionTraversalCallback& aCb)
|
|
{
|
|
/*
|
|
* We treat the zone as being gray. We handle non-gray GCthings in the
|
|
* zone by not reporting their children to the CC. The black-gray invariant
|
|
* ensures that any JS children will also be non-gray, and thus don't need to be
|
|
* added to the graph. For C++ children, not representing the edge from the
|
|
* non-gray JS GCthings to the C++ object will keep the child alive.
|
|
*
|
|
* We don't allow zone merging in a WantAllTraces CC, because then these
|
|
* assumptions don't hold.
|
|
*/
|
|
aCb.DescribeGCedNode(false, "JS Zone");
|
|
|
|
/*
|
|
* Every JS child of everything in the zone is either in the zone
|
|
* or is a cross-compartment wrapper. In the former case, we don't need to
|
|
* represent these edges in the CC graph because JS objects are not ref counted.
|
|
* In the latter case, the JS engine keeps a map of these wrappers, which we
|
|
* iterate over. Edges between compartments in the same zone will add
|
|
* unnecessary loop edges to the graph (bug 842137).
|
|
*/
|
|
TraversalTracer trc(mJSRuntime, aCb);
|
|
js::VisitGrayWrapperTargets(aZone, NoteJSChildGrayWrapperShim, &trc);
|
|
|
|
/*
|
|
* To find C++ children of things in the zone, we scan every JS Object in
|
|
* the zone. Only JS Objects can have C++ children.
|
|
*/
|
|
TraverseObjectShimClosure closure = { aCb, this };
|
|
js::IterateGrayObjects(aZone, TraverseObjectShim, &closure);
|
|
}
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::TraverseObjectShim(void* aData, JS::GCCellPtr aThing)
|
|
{
|
|
TraverseObjectShimClosure* closure =
|
|
static_cast<TraverseObjectShimClosure*>(aData);
|
|
|
|
MOZ_ASSERT(aThing.is<JSObject>());
|
|
closure->self->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_CPP,
|
|
aThing, closure->cb);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb)
|
|
{
|
|
// NB: This is here just to preserve the existing XPConnect order. I doubt it
|
|
// would hurt to do this after the JS holders.
|
|
TraverseAdditionalNativeRoots(aCb);
|
|
|
|
for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
|
|
void* holder = iter.Key();
|
|
nsScriptObjectTracer*& tracer = iter.Data();
|
|
|
|
bool noteRoot = false;
|
|
if (MOZ_UNLIKELY(aCb.WantAllTraces())) {
|
|
noteRoot = true;
|
|
} else {
|
|
tracer->Trace(holder,
|
|
TraceCallbackFunc(CheckParticipatesInCycleCollection),
|
|
¬eRoot);
|
|
}
|
|
|
|
if (noteRoot) {
|
|
aCb.NoteNativeRoot(holder, tracer);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::TraceBlackJS(JSTracer* aTracer, void* aData)
|
|
{
|
|
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
|
|
|
self->TraceNativeBlackRoots(aTracer);
|
|
}
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::TraceGrayJS(JSTracer* aTracer, void* aData)
|
|
{
|
|
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
|
|
|
// Mark these roots as gray so the CC can walk them later.
|
|
self->TraceNativeGrayRoots(aTracer);
|
|
}
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::GCCallback(JSRuntime* aRuntime,
|
|
JSGCStatus aStatus,
|
|
void* aData)
|
|
{
|
|
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
|
|
|
MOZ_ASSERT(aRuntime == self->Runtime());
|
|
|
|
self->OnGC(aStatus);
|
|
}
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::GCSliceCallback(JSRuntime* aRuntime,
|
|
JS::GCProgress aProgress,
|
|
const JS::GCDescription& aDesc)
|
|
{
|
|
CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
|
|
MOZ_ASSERT(self->Runtime() == aRuntime);
|
|
|
|
if (aProgress == JS::GC_CYCLE_END) {
|
|
JS::gcreason::Reason reason = aDesc.reason_;
|
|
NS_WARN_IF(NS_FAILED(DebuggerOnGCRunnable::Enqueue(aRuntime, aDesc)) &&
|
|
reason != JS::gcreason::SHUTDOWN_CC &&
|
|
reason != JS::gcreason::DESTROY_RUNTIME &&
|
|
reason != JS::gcreason::XPCONNECT_SHUTDOWN);
|
|
}
|
|
|
|
if (self->mPrevGCSliceCallback) {
|
|
self->mPrevGCSliceCallback(aRuntime, aProgress, aDesc);
|
|
}
|
|
}
|
|
|
|
class MinorGCMarker : public TimelineMarker
|
|
{
|
|
private:
|
|
JS::gcreason::Reason mReason;
|
|
|
|
public:
|
|
MinorGCMarker(MarkerTracingType aTracingType,
|
|
JS::gcreason::Reason aReason)
|
|
: TimelineMarker("MinorGC",
|
|
aTracingType,
|
|
MarkerStackRequest::NO_STACK)
|
|
, mReason(aReason)
|
|
{
|
|
MOZ_ASSERT(aTracingType == MarkerTracingType::START ||
|
|
aTracingType == MarkerTracingType::END);
|
|
}
|
|
|
|
MinorGCMarker(JS::GCNurseryProgress aProgress,
|
|
JS::gcreason::Reason aReason)
|
|
: TimelineMarker("MinorGC",
|
|
aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
|
|
? MarkerTracingType::START
|
|
: MarkerTracingType::END,
|
|
MarkerStackRequest::NO_STACK)
|
|
, mReason(aReason)
|
|
{ }
|
|
|
|
virtual void
|
|
AddDetails(JSContext* aCx,
|
|
dom::ProfileTimelineMarker& aMarker) override
|
|
{
|
|
TimelineMarker::AddDetails(aCx, aMarker);
|
|
|
|
if (GetTracingType() == MarkerTracingType::START) {
|
|
auto reason = JS::gcreason::ExplainReason(mReason);
|
|
aMarker.mCauseName.Construct(NS_ConvertUTF8toUTF16(reason));
|
|
}
|
|
}
|
|
|
|
virtual UniquePtr<AbstractTimelineMarker>
|
|
Clone() override
|
|
{
|
|
auto clone = MakeUnique<MinorGCMarker>(GetTracingType(), mReason);
|
|
clone->SetCustomTime(GetTime());
|
|
return UniquePtr<AbstractTimelineMarker>(Move(clone));
|
|
}
|
|
};
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::GCNurseryCollectionCallback(JSRuntime* aRuntime,
|
|
JS::GCNurseryProgress aProgress,
|
|
JS::gcreason::Reason aReason)
|
|
{
|
|
CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
|
|
MOZ_ASSERT(self->Runtime() == aRuntime);
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
|
if (timelines && !timelines->IsEmpty()) {
|
|
UniquePtr<AbstractTimelineMarker> abstractMarker(
|
|
MakeUnique<MinorGCMarker>(aProgress, aReason));
|
|
timelines->AddMarkerForAllObservedDocShells(abstractMarker);
|
|
}
|
|
|
|
if (self->mPrevGCNurseryCollectionCallback) {
|
|
self->mPrevGCNurseryCollectionCallback(aRuntime, aProgress, aReason);
|
|
}
|
|
}
|
|
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext* aContext,
|
|
void* aData)
|
|
{
|
|
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
|
|
|
MOZ_ASSERT(JS_GetRuntime(aContext) == self->Runtime());
|
|
|
|
self->OnOutOfMemory();
|
|
}
|
|
|
|
/* static */ void
|
|
CycleCollectedJSRuntime::LargeAllocationFailureCallback(void* aData)
|
|
{
|
|
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
|
|
|
self->OnLargeAllocationFailure();
|
|
}
|
|
|
|
/* static */ bool
|
|
CycleCollectedJSRuntime::ContextCallback(JSContext* aContext,
|
|
unsigned aOperation,
|
|
void* aData)
|
|
{
|
|
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
|
|
|
MOZ_ASSERT(JS_GetRuntime(aContext) == self->Runtime());
|
|
|
|
return self->CustomContextCallback(aContext, aOperation);
|
|
}
|
|
|
|
struct JsGcTracer : public TraceCallbacks
|
|
{
|
|
virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
virtual void Trace(JSObject** aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
js::UnsafeTraceManuallyBarrieredEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
|
|
}
|
|
};
|
|
|
|
void
|
|
mozilla::TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer)
|
|
{
|
|
nsXPCOMCycleCollectionParticipant* participant = nullptr;
|
|
CallQueryInterface(aHolder, &participant);
|
|
participant->Trace(aHolder, JsGcTracer(), aTracer);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer* aTracer)
|
|
{
|
|
// NB: This is here just to preserve the existing XPConnect order. I doubt it
|
|
// would hurt to do this after the JS holders.
|
|
TraceAdditionalNativeGrayRoots(aTracer);
|
|
|
|
for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
|
|
void* holder = iter.Key();
|
|
nsScriptObjectTracer*& tracer = iter.Data();
|
|
tracer->Trace(holder, JsGcTracer(), aTracer);
|
|
}
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
|
|
{
|
|
mJSHolders.Put(aHolder, aTracer);
|
|
}
|
|
|
|
struct ClearJSHolder : public TraceCallbacks
|
|
{
|
|
virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*, void*) const override
|
|
{
|
|
aPtr->setUndefined();
|
|
}
|
|
|
|
virtual void Trace(JS::Heap<jsid>* aPtr, const char*, void*) const override
|
|
{
|
|
*aPtr = JSID_VOID;
|
|
}
|
|
|
|
virtual void Trace(JS::Heap<JSObject*>* aPtr, const char*, void*) const override
|
|
{
|
|
*aPtr = nullptr;
|
|
}
|
|
|
|
virtual void Trace(JSObject** aPtr, const char* aName,
|
|
void* aClosure) const override
|
|
{
|
|
*aPtr = nullptr;
|
|
}
|
|
|
|
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*, void*) const override
|
|
{
|
|
*aPtr = nullptr;
|
|
}
|
|
|
|
virtual void Trace(JS::Heap<JSString*>* aPtr, const char*, void*) const override
|
|
{
|
|
*aPtr = nullptr;
|
|
}
|
|
|
|
virtual void Trace(JS::Heap<JSScript*>* aPtr, const char*, void*) const override
|
|
{
|
|
*aPtr = nullptr;
|
|
}
|
|
|
|
virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char*, void*) const override
|
|
{
|
|
*aPtr = nullptr;
|
|
}
|
|
};
|
|
|
|
void
|
|
CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder)
|
|
{
|
|
nsScriptObjectTracer* tracer = mJSHolders.Get(aHolder);
|
|
if (!tracer) {
|
|
return;
|
|
}
|
|
tracer->Trace(aHolder, ClearJSHolder(), nullptr);
|
|
mJSHolders.Remove(aHolder);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
bool
|
|
CycleCollectedJSRuntime::IsJSHolder(void* aHolder)
|
|
{
|
|
return mJSHolders.Get(aHolder, nullptr);
|
|
}
|
|
|
|
static void
|
|
AssertNoGcThing(JS::GCCellPtr aGCThing, const char* aName, void* aClosure)
|
|
{
|
|
MOZ_ASSERT(!aGCThing);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder)
|
|
{
|
|
nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
|
|
if (tracer) {
|
|
tracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing), nullptr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
already_AddRefed<nsIException>
|
|
CycleCollectedJSRuntime::GetPendingException() const
|
|
{
|
|
nsCOMPtr<nsIException> out = mPendingException;
|
|
return out.forget();
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::SetPendingException(nsIException* aException)
|
|
{
|
|
mPendingException = aException;
|
|
}
|
|
|
|
std::queue<nsCOMPtr<nsIRunnable>>&
|
|
CycleCollectedJSRuntime::GetPromiseMicroTaskQueue()
|
|
{
|
|
return mPromiseMicroTaskQueue;
|
|
}
|
|
|
|
nsCycleCollectionParticipant*
|
|
CycleCollectedJSRuntime::GCThingParticipant()
|
|
{
|
|
return &mGCThingCycleCollectorGlobal;
|
|
}
|
|
|
|
nsCycleCollectionParticipant*
|
|
CycleCollectedJSRuntime::ZoneParticipant()
|
|
{
|
|
return &mJSZoneCycleCollectorGlobal;
|
|
}
|
|
|
|
nsresult
|
|
CycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb)
|
|
{
|
|
TraverseNativeRoots(aCb);
|
|
|
|
NoteWeakMapsTracer trc(mJSRuntime, aCb);
|
|
js::TraceWeakMaps(&trc);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/*
|
|
* Return true if there exists a JSContext with a default global whose current
|
|
* inner is gray. The intent is to look for JS Object windows. We don't merge
|
|
* system compartments, so we don't use them to trigger merging CCs.
|
|
*/
|
|
bool
|
|
CycleCollectedJSRuntime::UsefulToMergeZones() const
|
|
{
|
|
if (!NS_IsMainThread()) {
|
|
return false;
|
|
}
|
|
|
|
JSContext* iter = nullptr;
|
|
JSContext* cx;
|
|
JSAutoRequest ar(nsContentUtils::GetSafeJSContext());
|
|
while ((cx = JS_ContextIterator(mJSRuntime, &iter))) {
|
|
// Skip anything without an nsIScriptContext.
|
|
nsIScriptContext* scx = GetScriptContextFromJSContext(cx);
|
|
JS::RootedObject obj(cx, scx ? scx->GetWindowProxyPreserveColor() : nullptr);
|
|
if (!obj) {
|
|
continue;
|
|
}
|
|
MOZ_ASSERT(js::IsWindowProxy(obj));
|
|
// Grab the global from the WindowProxy.
|
|
obj = js::ToWindowIfWindowProxy(obj);
|
|
MOZ_ASSERT(JS_IsGlobalObject(obj));
|
|
if (JS::ObjectIsMarkedGray(obj) &&
|
|
!js::IsSystemCompartment(js::GetObjectCompartment(obj))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::FixWeakMappingGrayBits() const
|
|
{
|
|
MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime),
|
|
"Don't call FixWeakMappingGrayBits during a GC.");
|
|
FixWeakMappingGrayBitsTracer fixer(mJSRuntime);
|
|
fixer.FixAll();
|
|
}
|
|
|
|
bool
|
|
CycleCollectedJSRuntime::AreGCGrayBitsValid() const
|
|
{
|
|
return js::AreGCGrayBitsValid(mJSRuntime);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::GarbageCollect(uint32_t aReason) const
|
|
{
|
|
MOZ_ASSERT(aReason < JS::gcreason::NUM_REASONS);
|
|
JS::gcreason::Reason gcreason = static_cast<JS::gcreason::Reason>(aReason);
|
|
|
|
JS::PrepareForFullGC(mJSRuntime);
|
|
JS::GCForReason(mJSRuntime, GC_NORMAL, gcreason);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::JSObjectsTenured(JSRuntime* aRuntime)
|
|
{
|
|
for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
|
|
nsWrapperCache* cache = iter.Get();
|
|
JSObject* wrapper = cache->GetWrapperPreserveColor();
|
|
MOZ_ASSERT(wrapper);
|
|
if (!JS::ObjectIsTenured(wrapper)) {
|
|
MOZ_ASSERT(!cache->PreservingWrapper());
|
|
const JSClass* jsClass = js::GetObjectJSClass(wrapper);
|
|
jsClass->finalize(nullptr, wrapper);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
for (auto iter = mPreservedNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
|
|
MOZ_ASSERT(JS::ObjectIsTenured(iter.Get().get()));
|
|
}
|
|
#endif
|
|
|
|
mNurseryObjects.Clear();
|
|
mPreservedNurseryObjects.Clear();
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache)
|
|
{
|
|
MOZ_ASSERT(aCache);
|
|
MOZ_ASSERT(aCache->GetWrapperPreserveColor());
|
|
MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor()));
|
|
mNurseryObjects.InfallibleAppend(aCache);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject* aWrapper)
|
|
{
|
|
mPreservedNurseryObjects.InfallibleAppend(
|
|
JS::PersistentRooted<JSObject*>(mJSRuntime, aWrapper));
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
|
|
DeferredFinalizeFunction aFunc,
|
|
void* aThing)
|
|
{
|
|
void* thingArray = nullptr;
|
|
bool hadThingArray = mDeferredFinalizerTable.Get(aFunc, &thingArray);
|
|
|
|
thingArray = aAppendFunc(thingArray, aThing);
|
|
if (!hadThingArray) {
|
|
mDeferredFinalizerTable.Put(aFunc, thingArray);
|
|
}
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
|
|
{
|
|
typedef DeferredFinalizerImpl<nsISupports> Impl;
|
|
DeferredFinalize(Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize,
|
|
aSupports);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile)
|
|
{
|
|
js::DumpHeap(Runtime(), aFile, js::CollectNurseryBeforeDump);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::ProcessStableStateQueue()
|
|
{
|
|
MOZ_RELEASE_ASSERT(!mDoingStableStates);
|
|
mDoingStableStates = true;
|
|
|
|
for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
|
|
nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
|
|
event->Run();
|
|
}
|
|
|
|
mStableStateEvents.Clear();
|
|
mDoingStableStates = false;
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
|
|
{
|
|
MOZ_RELEASE_ASSERT(!mDoingStableStates);
|
|
mDoingStableStates = true;
|
|
|
|
nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
|
|
|
|
for (uint32_t i = 0; i < localQueue.Length(); ++i)
|
|
{
|
|
RunInMetastableStateData& data = localQueue[i];
|
|
if (data.mRecursionDepth != aRecursionDepth) {
|
|
continue;
|
|
}
|
|
|
|
{
|
|
nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
|
|
runnable->Run();
|
|
}
|
|
|
|
localQueue.RemoveElementAt(i--);
|
|
}
|
|
|
|
// If the queue has events in it now, they were added from something we called,
|
|
// so they belong at the end of the queue.
|
|
localQueue.AppendElements(mMetastableStateEvents);
|
|
localQueue.SwapElements(mMetastableStateEvents);
|
|
mDoingStableStates = false;
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::AfterProcessTask(uint32_t aRecursionDepth)
|
|
{
|
|
// See HTML 6.1.4.2 Processing model
|
|
|
|
// Execute any events that were waiting for a microtask to complete.
|
|
// This is not (yet) in the spec.
|
|
ProcessMetastableStateQueue(aRecursionDepth);
|
|
|
|
// Step 4.1: Execute microtasks.
|
|
if (NS_IsMainThread()) {
|
|
nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
|
|
}
|
|
|
|
Promise::PerformMicroTaskCheckpoint();
|
|
|
|
// Step 4.2 Execute any events that were waiting for a stable state.
|
|
ProcessStableStateQueue();
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::AfterProcessMicrotask()
|
|
{
|
|
AfterProcessMicrotask(RecursionDepth());
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::AfterProcessMicrotask(uint32_t aRecursionDepth)
|
|
{
|
|
// Between microtasks, execute any events that were waiting for a microtask
|
|
// to complete.
|
|
ProcessMetastableStateQueue(aRecursionDepth);
|
|
}
|
|
|
|
uint32_t
|
|
CycleCollectedJSRuntime::RecursionDepth()
|
|
{
|
|
return mOwningThread->RecursionDepth();
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
|
|
{
|
|
MOZ_ASSERT(mJSRuntime);
|
|
mStableStateEvents.AppendElement(Move(aRunnable));
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
|
|
{
|
|
RunInMetastableStateData data;
|
|
data.mRunnable = aRunnable;
|
|
|
|
MOZ_ASSERT(mOwningThread);
|
|
data.mRecursionDepth = RecursionDepth();
|
|
|
|
// There must be an event running to get here.
|
|
#ifndef MOZ_WIDGET_COCOA
|
|
MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
|
|
#endif
|
|
|
|
mMetastableStateEvents.AppendElement(Move(data));
|
|
}
|
|
|
|
IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
|
|
DeferredFinalizerTable& aFinalizers)
|
|
: mRuntime(aRt)
|
|
, mFinalizeFunctionToRun(0)
|
|
, mReleasing(false)
|
|
{
|
|
for (auto iter = aFinalizers.Iter(); !iter.Done(); iter.Next()) {
|
|
DeferredFinalizeFunction& function = iter.Key();
|
|
void*& data = iter.Data();
|
|
|
|
DeferredFinalizeFunctionHolder* holder =
|
|
mDeferredFinalizeFunctions.AppendElement();
|
|
holder->run = function;
|
|
holder->data = data;
|
|
|
|
iter.Remove();
|
|
}
|
|
}
|
|
|
|
IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
|
|
{
|
|
MOZ_ASSERT(this != mRuntime->mFinalizeRunnable);
|
|
}
|
|
|
|
void
|
|
IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
|
|
{
|
|
if (mReleasing) {
|
|
NS_WARNING("Re-entering ReleaseNow");
|
|
return;
|
|
}
|
|
{
|
|
mozilla::AutoRestore<bool> ar(mReleasing);
|
|
mReleasing = true;
|
|
MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
|
|
"We should have at least ReleaseSliceNow to run");
|
|
MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
|
|
"No more finalizers to run?");
|
|
|
|
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
|
|
TimeStamp started = TimeStamp::Now();
|
|
bool timeout = false;
|
|
do {
|
|
const DeferredFinalizeFunctionHolder& function =
|
|
mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
|
|
if (aLimited) {
|
|
bool done = false;
|
|
while (!timeout && !done) {
|
|
/*
|
|
* We don't want to read the clock too often, so we try to
|
|
* release slices of 100 items.
|
|
*/
|
|
done = function.run(100, function.data);
|
|
timeout = TimeStamp::Now() - started >= sliceTime;
|
|
}
|
|
if (done) {
|
|
++mFinalizeFunctionToRun;
|
|
}
|
|
if (timeout) {
|
|
break;
|
|
}
|
|
} else {
|
|
while (!function.run(UINT32_MAX, function.data));
|
|
++mFinalizeFunctionToRun;
|
|
}
|
|
} while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
|
|
}
|
|
|
|
if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
|
|
MOZ_ASSERT(mRuntime->mFinalizeRunnable == this);
|
|
mDeferredFinalizeFunctions.Clear();
|
|
// NB: This may delete this!
|
|
mRuntime->mFinalizeRunnable = nullptr;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IncrementalFinalizeRunnable::Run()
|
|
{
|
|
if (mRuntime->mFinalizeRunnable != this) {
|
|
/* These items were already processed synchronously in JSGC_END. */
|
|
MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());
|
|
return NS_OK;
|
|
}
|
|
|
|
TimeStamp start = TimeStamp::Now();
|
|
ReleaseNow(true);
|
|
|
|
if (mDeferredFinalizeFunctions.Length()) {
|
|
nsresult rv = NS_DispatchToCurrentThread(this);
|
|
if (NS_FAILED(rv)) {
|
|
ReleaseNow(false);
|
|
}
|
|
}
|
|
|
|
uint32_t duration = (uint32_t)((TimeStamp::Now() - start).ToMilliseconds());
|
|
Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC, duration);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::FinalizeDeferredThings(DeferredFinalizeType aType)
|
|
{
|
|
/*
|
|
* If the previous GC created a runnable to finalize objects
|
|
* incrementally, and if it hasn't finished yet, finish it now. We
|
|
* don't want these to build up. We also don't want to allow any
|
|
* existing incremental finalize runnables to run after a
|
|
* non-incremental GC, since they are often used to detect leaks.
|
|
*/
|
|
if (mFinalizeRunnable) {
|
|
mFinalizeRunnable->ReleaseNow(false);
|
|
if (mFinalizeRunnable) {
|
|
// If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
|
|
// we need to just continue processing it.
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mDeferredFinalizerTable.Count() == 0) {
|
|
return;
|
|
}
|
|
|
|
mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
|
|
mDeferredFinalizerTable);
|
|
|
|
// Everything should be gone now.
|
|
MOZ_ASSERT(mDeferredFinalizerTable.Count() == 0);
|
|
|
|
if (aType == FinalizeIncrementally) {
|
|
NS_DispatchToCurrentThread(mFinalizeRunnable);
|
|
} else {
|
|
mFinalizeRunnable->ReleaseNow(false);
|
|
MOZ_ASSERT(!mFinalizeRunnable);
|
|
}
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
|
|
OOMState aNewState)
|
|
{
|
|
*aStatePtr = aNewState;
|
|
#ifdef MOZ_CRASHREPORTER
|
|
CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState
|
|
? NS_LITERAL_CSTRING("JSOutOfMemory")
|
|
: NS_LITERAL_CSTRING("JSLargeAllocationFailure"),
|
|
aNewState == OOMState::Reporting
|
|
? NS_LITERAL_CSTRING("Reporting")
|
|
: aNewState == OOMState::Reported
|
|
? NS_LITERAL_CSTRING("Reported")
|
|
: NS_LITERAL_CSTRING("Recovered"));
|
|
#endif
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
|
|
{
|
|
switch (aStatus) {
|
|
case JSGC_BEGIN:
|
|
nsCycleCollector_prepareForGarbageCollection();
|
|
break;
|
|
case JSGC_END: {
|
|
#ifdef MOZ_CRASHREPORTER
|
|
if (mOutOfMemoryState == OOMState::Reported) {
|
|
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Recovered);
|
|
}
|
|
if (mLargeAllocationFailureState == OOMState::Reported) {
|
|
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Recovered);
|
|
}
|
|
#endif
|
|
|
|
// Do any deferred finalization of native objects.
|
|
FinalizeDeferredThings(JS::WasIncrementalGC(mJSRuntime) ? FinalizeIncrementally :
|
|
FinalizeNow);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_CRASH();
|
|
}
|
|
|
|
CustomGCCallback(aStatus);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::OnOutOfMemory()
|
|
{
|
|
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reporting);
|
|
CustomOutOfMemoryCallback();
|
|
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reported);
|
|
}
|
|
|
|
void
|
|
CycleCollectedJSRuntime::OnLargeAllocationFailure()
|
|
{
|
|
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reporting);
|
|
CustomLargeAllocationFailureCallback();
|
|
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);
|
|
}
|