import changes from `dev' branch of rmottola/Arctic-Fox:

- 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)
This commit is contained in:
2023-08-27 23:02:30 +08:00
parent f47d10b172
commit f18df9acf8
363 changed files with 16364 additions and 6679 deletions
@@ -0,0 +1,174 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that user agent styles are inspectable via rule view if
// it is preffed on.
var PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
const { PrefObserver } = require("devtools/client/styleeditor/utils");
const TEST_URI = TEST_URL_ROOT + "doc_author-sheet.html";
const TEST_DATA = [
{
selector: "blockquote",
numUserRules: 1,
numUARules: 0
},
{
selector: "pre",
numUserRules: 1,
numUARules: 0
},
{
selector: "input[type=range]",
numUserRules: 1,
numUARules: 0
},
{
selector: "input[type=number]",
numUserRules: 1,
numUARules: 0
},
{
selector: "input[type=color]",
numUserRules: 1,
numUARules: 0
},
{
selector: "input[type=text]",
numUserRules: 1,
numUARules: 0
},
{
selector: "progress",
numUserRules: 1,
numUARules: 0
},
{
selector: "a",
numUserRules: 3,
numUARules: 0
}
];
add_task(function*() {
requestLongerTimeout(2);
info("Starting the test with the pref set to true before toolbox is opened");
yield setUserAgentStylesPref(true);
yield addTab(TEST_URI);
let {inspector, view} = yield openRuleView();
info("Making sure that UA styles are visible on initial load");
yield userAgentStylesVisible(inspector, view);
info("Making sure that setting the pref to false hides UA styles");
yield setUserAgentStylesPref(false);
yield userAgentStylesNotVisible(inspector, view);
info("Making sure that resetting the pref to true shows UA styles again");
yield setUserAgentStylesPref(true);
yield userAgentStylesVisible(inspector, view);
info("Resetting " + PREF_UA_STYLES);
Services.prefs.clearUserPref(PREF_UA_STYLES);
});
function* setUserAgentStylesPref(val) {
info("Setting the pref " + PREF_UA_STYLES + " to: " + val);
// Reset the pref and wait for PrefObserver to callback so UI
// has a chance to get updated.
let oncePrefChanged = promise.defer();
let prefObserver = new PrefObserver("devtools.");
prefObserver.on(PREF_UA_STYLES, oncePrefChanged.resolve);
Services.prefs.setBoolPref(PREF_UA_STYLES, val);
yield oncePrefChanged.promise;
prefObserver.off(PREF_UA_STYLES, oncePrefChanged.resolve);
}
function* userAgentStylesVisible(inspector, view) {
info("Making sure that user agent styles are currently visible");
let userRules;
let uaRules;
for (let data of TEST_DATA) {
yield selectNode(data.selector, inspector);
yield compareAppliedStylesWithUI(inspector, view, "ua");
userRules = view._elementStyle.rules.filter(rule=>rule.editor.isEditable);
uaRules = view._elementStyle.rules.filter(rule=>!rule.editor.isEditable);
is(userRules.length, data.numUserRules, "Correct number of user rules");
ok(uaRules.length > data.numUARules, "Has UA rules");
}
ok(userRules.some(rule=> rule.matchedSelectors.length === 1),
"There is an inline style for element in user styles");
ok(uaRules.some(rule=> rule.matchedSelectors.indexOf(":-moz-any-link")),
"There is a rule for :-moz-any-link");
ok(uaRules.some(rule=> rule.matchedSelectors.indexOf("*|*:link")),
"There is a rule for *|*:link");
ok(uaRules.some(rule=> rule.matchedSelectors.length === 1),
"Inline styles for ua styles");
}
function* userAgentStylesNotVisible(inspector, view) {
info("Making sure that user agent styles are not currently visible");
let userRules;
let uaRules;
for (let data of TEST_DATA) {
yield selectNode(data.selector, inspector);
yield compareAppliedStylesWithUI(inspector, view);
userRules = view._elementStyle.rules.filter(rule=>rule.editor.isEditable);
uaRules = view._elementStyle.rules.filter(rule=>!rule.editor.isEditable);
is(userRules.length, data.numUserRules, "Correct number of user rules");
is(uaRules.length, data.numUARules, "No UA rules");
}
}
function* compareAppliedStylesWithUI(inspector, view, filter) {
info("Making sure that UI is consistent with pageStyle.getApplied");
let entries = yield inspector.pageStyle.getApplied(
inspector.selection.nodeFront, {
inherited: true,
matchedSelectors: true,
filter: filter
});
// We may see multiple entries that map to a given rule; filter the
// duplicates here to match what the UI does.
let entryMap = new Map();
for (let entry of entries) {
entryMap.set(entry.rule, entry);
}
entries = [...entryMap.values()];
let elementStyle = view._elementStyle;
is(elementStyle.rules.length, entries.length,
"Should have correct number of rules (" + entries.length + ")");
entries = entries.sort((a, b) => {
return (a.pseudoElement || "z") > (b.pseudoElement || "z");
});
entries.forEach((entry, i) => {
let elementStyleRule = elementStyle.rules[i];
is(elementStyleRule.inherited, entry.inherited,
"Same inherited (" + entry.inherited + ")");
is(elementStyleRule.isSystem, entry.isSystem,
"Same isSystem (" + entry.isSystem + ")");
is(elementStyleRule.editor.isEditable, !entry.isSystem,
"Editor isEditable opposite of UA (" + entry.isSystem + ")");
});
}
@@ -0,0 +1,39 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>authored sheet test</title>
<style>
pre a {
color: orange;
}
</style>
<script>
var gIOService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
.getService(SpecialPowers.Ci.nsIIOService)
var style = "data:text/css,a { background-color: seagreen; }";
var uri = gIOService.newURI(style, null, null);
var windowUtils = SpecialPowers.wrap(window)
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
windowUtils.loadSheet(uri, windowUtils.AUTHOR_SHEET);
</script>
</head>
<body>
<input type=text placeholder=test></input>
<input type=color></input>
<input type=range></input>
<input type=number></input>
<progress></progress>
<blockquote type=cite>
<pre _moz_quote=true>
inspect <a href="foo">user agent</a> styles
</pre>
</blockquote>
</body>
</html>
@@ -0,0 +1,32 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get "MinorGC" markers when we continue to steadily allocate
* objects.
*/
const { PerformanceFront } = require("devtools/server/actors/performance");
add_task(function*() {
// This test runs very slowly on linux32 debug EC2 instances.
requestLongerTimeout(2);
let doc = yield addTab(MAIN_DOMAIN + "doc_allocations.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true });
let markers = yield waitForMarkerType(front, ["MinorGC"]);
yield front.stopRecording(rec);
ok(markers.some(m => m.name === "MinorGC" && m.causeName),
"got some MinorGC markers");
yield closeDebuggerClient(client);
gBrowser.removeCurrentTab();
});
@@ -3,7 +3,7 @@
"use strict";
let URL = '<!DOCTYPE html><style>' +
var URL = '<!DOCTYPE html><style>' +
'body {margin:0; padding: 0;} ' +
'div {width:100px;height:100px;background:red;} ' +
'.resize-change-color {width:50px;height:50px;background:blue;} ' +
@@ -12,4 +12,4 @@ let URL = '<!DOCTYPE html><style>' +
'</style><div></div>';
URL = "data:text/html;charset=utf8," + encodeURIComponent(URL);
let test = makeTimelineTest("browser_timelineMarkers-frame-02.js", URL);
var test = makeTimelineTest("browser_timelineMarkers-frame-02.js", URL);
@@ -11,7 +11,13 @@ function rectangleContains(rect, x, y, width, height) {
rect.height >= height;
}
let TESTS = [{
function sanitizeMarkers(list) {
// These markers are currently gathered from all docshells, which may
// interfere with this test.
return list.filter(e => e.name != "Worker" && e.name != "MinorGC");
}
var TESTS = [{
desc: "Changing the width of the test element",
searchFor: "Paint",
setup: function(docShell) {
@@ -19,6 +25,7 @@ let TESTS = [{
div.setAttribute("class", "resize-change-color");
},
check: function(markers) {
markers = sanitizeMarkers(markers);
ok(markers.length > 0, "markers were returned");
console.log(markers);
info(JSON.stringify(markers.filter(m => m.name == "Paint")));
@@ -40,6 +47,7 @@ let TESTS = [{
div.setAttribute("class", "change-color");
},
check: function(markers) {
markers = sanitizeMarkers(markers);
ok(markers.length > 0, "markers were returned");
ok(!markers.some(m => m.name == "Reflow"), "markers doesn't include Reflow");
ok(markers.some(m => m.name == "Paint"), "markers includes Paint");
@@ -59,6 +67,7 @@ let TESTS = [{
div.setAttribute("class", "change-color add-class");
},
check: function(markers) {
markers = sanitizeMarkers(markers);
ok(markers.length > 0, "markers were returned");
ok(!markers.some(m => m.name == "Reflow"), "markers doesn't include Reflow");
ok(!markers.some(m => m.name == "Paint"), "markers doesn't include Paint");
@@ -84,6 +93,7 @@ let TESTS = [{
}, 100);
},
check: function(markers) {
markers = sanitizeMarkers(markers);
is(markers.length, 2, "Got 2 markers");
is(markers[0].name, "ConsoleTime", "Got first ConsoleTime marker");
is(markers[0].causeName, "FOO", "Got ConsoleTime FOO detail");
@@ -105,7 +115,7 @@ let TESTS = [{
content.console.timeStamp(undefined);
},
check: function (markers) {
markers = markers.filter(e => e.name != "Worker");
markers = sanitizeMarkers(markers);
is(markers.length, 4, "Got 4 markers");
is(markers[0].name, "TimeStamp", "Got Timestamp marker");
is(markers[0].causeName, "paper", "Got Timestamp label value");
+3 -2
View File
@@ -685,7 +685,8 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(Console)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END
Console::Console(nsPIDOMWindow* aWindow)
@@ -709,7 +710,7 @@ Console::Console(nsPIDOMWindow* aWindow)
if (NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, "inner-window-destroyed", false);
obs->AddObserver(this, "inner-window-destroyed", true);
}
}
+3 -1
View File
@@ -14,6 +14,7 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsWrapperCache.h"
#include "nsDOMNavigationTiming.h"
#include "nsPIDOMWindow.h"
@@ -29,12 +30,13 @@ struct ConsoleStackEntry;
class Console final : public nsIObserver
, public nsWrapperCache
, public nsSupportsWeakReference
{
~Console();
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Console)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console, nsIObserver)
NS_DECL_NSIOBSERVER
explicit Console(nsPIDOMWindow* aWindow);
+7 -7
View File
@@ -26,7 +26,8 @@
namespace mozilla {
namespace dom {
static mozilla::ThreadLocal<ScriptSettingsStackEntry*> sScriptSettingsTLS;
static MOZ_THREAD_LOCAL(ScriptSettingsStackEntry*) sScriptSettingsTLS;
static bool sScriptSettingsTLSInitialized;
class ScriptSettingsStack {
public:
@@ -94,14 +95,13 @@ UnuseEntryScriptProfiling()
void
InitScriptSettings()
{
if (!sScriptSettingsTLS.initialized()) {
bool success = sScriptSettingsTLS.init();
if (!success) {
MOZ_CRASH();
}
bool success = sScriptSettingsTLS.init();
if (!success) {
MOZ_CRASH();
}
sScriptSettingsTLS.set(nullptr);
sScriptSettingsTLSInitialized = true;
}
void
@@ -113,7 +113,7 @@ DestroyScriptSettings()
bool
ScriptSettingsInitialized()
{
return sScriptSettingsTLS.initialized();
return sScriptSettingsTLSInitialized;
}
ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject *aGlobal,
+3
View File
@@ -1555,6 +1555,9 @@ nsFrameLoader::DestroyComplete()
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
}
mMessageManager = nullptr;
mChildMessageManager = nullptr;
}
NS_IMETHODIMP
+16 -23
View File
@@ -93,8 +93,7 @@ ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...)
bool
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
const ErrNum aErrorNumber,
const char* aInterfaceName)
bool aSecurityError, const char* aInterfaceName)
{
NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
// This should only be called for DOM methods/getters/setters, which
@@ -109,19 +108,22 @@ ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
if (!funcNameStr.init(aCx, funcName)) {
return false;
}
MOZ_RELEASE_ASSERT(GetErrorArgCount(aErrorNumber) <= 2);
const ErrNum errorNumber = aSecurityError ?
MSG_METHOD_THIS_UNWRAPPING_DENIED :
MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) <= 2);
JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
static_cast<const unsigned>(aErrorNumber),
static_cast<const unsigned>(errorNumber),
funcNameStr.get(), ifaceName.get());
return false;
}
bool
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
const ErrNum aErrorNumber,
bool aSecurityError,
prototypes::ID aProtoId)
{
return ThrowInvalidThis(aCx, aArgs, aErrorNumber,
return ThrowInvalidThis(aCx, aArgs, aSecurityError,
NamesOfInterfacesWithProtos(aProtoId));
}
@@ -2633,9 +2635,7 @@ GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
if (!args.thisv().isObject()) {
return ThrowInvalidThis(cx, args,
MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
protoID);
return ThrowInvalidThis(cx, args, false, protoID);
}
JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
@@ -2644,7 +2644,7 @@ GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
if (NS_FAILED(rv)) {
return ThrowInvalidThis(cx, args,
GetInvalidThisErrorForGetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
protoID);
}
}
@@ -2667,9 +2667,7 @@ GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
if (!args.thisv().isObject()) {
return ThrowInvalidThis(cx, args,
MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
protoID);
return ThrowInvalidThis(cx, args, false, protoID);
}
JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
@@ -2678,7 +2676,7 @@ GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
if (NS_FAILED(rv)) {
return ThrowInvalidThis(cx, args,
GetInvalidThisErrorForSetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
protoID);
}
}
@@ -2704,9 +2702,7 @@ GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
if (!args.thisv().isObject()) {
return ThrowInvalidThis(cx, args,
MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
protoID);
return ThrowInvalidThis(cx, args, false, protoID);
}
JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
@@ -2715,7 +2711,7 @@ GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
if (NS_FAILED(rv)) {
return ThrowInvalidThis(cx, args,
GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
protoID);
}
}
@@ -2742,9 +2738,7 @@ GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
if (!args.thisv().isObject()) {
ThrowInvalidThis(cx, args,
MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
protoID);
ThrowInvalidThis(cx, args, false, protoID);
return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
args.rval());
}
@@ -2754,8 +2748,7 @@ GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp
{
nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
if (NS_FAILED(rv)) {
ThrowInvalidThis(cx, args,
GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
ThrowInvalidThis(cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
protoID);
return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
args.rval());
+4 -27
View File
@@ -61,36 +61,13 @@ UnwrapArg(JS::Handle<JSObject*> src, Interface** ppArg)
reinterpret_cast<void**>(ppArg));
}
inline const ErrNum
GetInvalidThisErrorForMethod(bool aSecurityError)
{
return aSecurityError ? MSG_METHOD_THIS_UNWRAPPING_DENIED :
MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
}
inline const ErrNum
GetInvalidThisErrorForGetter(bool aSecurityError)
{
return aSecurityError ? MSG_GETTER_THIS_UNWRAPPING_DENIED :
MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
}
inline const ErrNum
GetInvalidThisErrorForSetter(bool aSecurityError)
{
return aSecurityError ? MSG_SETTER_THIS_UNWRAPPING_DENIED :
MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
}
bool
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
bool aSecurityError, const char* aInterfaceName);
bool
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
const ErrNum aErrorNumber,
const char* aInterfaceName);
bool
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
const ErrNum aErrorNumber,
prototypes::ID aProtoId);
bool aSecurityError, prototypes::ID aProtoId);
// Returns true if the JSClass is used for DOM objects.
inline bool
+6 -6
View File
@@ -8105,7 +8105,7 @@ class CGGenericMethod(CGAbstractBindingMethod):
"""
def __init__(self, descriptor, allowCrossOriginThis=False):
unwrapFailureCode = (
'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n' %
'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
descriptor.interface.identifier.name)
name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod"
CGAbstractBindingMethod.__init__(self, descriptor, name,
@@ -8136,7 +8136,7 @@ class CGGenericPromiseReturningMethod(CGAbstractBindingMethod):
"""
def __init__(self, descriptor):
unwrapFailureCode = dedent("""
ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n
ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n
return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
args.rval());\n""" %
descriptor.interface.identifier.name)
@@ -8477,7 +8477,7 @@ class CGGenericGetter(CGAbstractBindingMethod):
else:
name = "genericGetter"
unwrapFailureCode = (
'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForGetter(%%(securityError)s), "%s");\n' %
'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
descriptor.interface.identifier.name)
CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
unwrapFailureCode,
@@ -8608,7 +8608,7 @@ class CGGenericSetter(CGAbstractBindingMethod):
else:
name = "genericSetter"
unwrapFailureCode = (
'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForSetter(%%(securityError)s), "%s");\n' %
'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
descriptor.interface.identifier.name)
CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
@@ -8772,7 +8772,7 @@ class CGMemberJITInfo(CGThing):
{
{ ${opName} },
{ prototypes::id::${name} },
PrototypeTraits<prototypes::id::${name}>::Depth,
{ PrototypeTraits<prototypes::id::${name}>::Depth },
JSJitInfo::${opType},
JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
${returnType}, /* returnType. Not relevant for setters. */
@@ -9152,7 +9152,7 @@ class CGStaticMethodJitinfo(CGGeneric):
"\n"
"static const JSJitInfo %s_methodinfo = {\n"
" { (JSJitGetterOp)%s },\n"
" { prototypes::id::_ID_Count }, 0, JSJitInfo::StaticMethod,\n"
" { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n"
" JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n"
" false, false, 0\n"
"};\n" %
-4
View File
@@ -29,10 +29,6 @@ MSG_DEF(MSG_NOT_CALLABLE, 1, JSEXN_TYPEERR, "{0} is not callable.")
MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "{0} does not implement interface {1}.")
MSG_DEF(MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' called on an object that does not implement interface {1}.")
MSG_DEF(MSG_METHOD_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' denied.")
MSG_DEF(MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' getter called on an object that does not implement interface {1}.")
MSG_DEF(MSG_GETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' getter denied.")
MSG_DEF(MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' setter called on an object that does not implement interface {1}.")
MSG_DEF(MSG_SETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' setter denied.")
MSG_DEF(MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 1, JSEXN_TYPEERR, "\"this\" object does not implement interface {0}.")
MSG_DEF(MSG_NOT_IN_UNION, 2, JSEXN_TYPEERR, "{0} could not be converted to any of: {1}.")
MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Illegal constructor.")
+2 -1
View File
@@ -68,4 +68,5 @@ skip-if = debug == false
[test_jsimplemented_eventhandler.html]
skip-if = debug == false
[test_iterable.html]
skip-if = debug == false
skip-if = debug == false
[test_oom_reporting.html]
@@ -18,10 +18,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=882653
"'appendChild' called on an object that does not implement interface Node.",
"bogus method this object" ],
[ 'Object.getOwnPropertyDescriptor(Document.prototype, "documentElement").get.call({})',
"'documentElement' getter called on an object that does not implement interface Document.",
"'get documentElement' called on an object that does not implement interface Document.",
"bogus getter this object" ],
[ 'Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML").set.call({})',
"'innerHTML' setter called on an object that does not implement interface Element.",
"'set innerHTML' called on an object that does not implement interface Element.",
"bogus setter this object" ],
[ 'document.documentElement.appendChild(5)',
"Argument 1 of Node.appendChild is not an object.",
+42
View File
@@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug **/
SimpleTest.waitForExplicitFinish();
window.length = 0xffffffff;
SimpleTest.expectUncaughtException();
setTimeout(Array.prototype.splice, 0, undefined);
addEventListener("error", function(e) {
is(e.type, "error", "Should have an error event");
is(e.message, "uncaught exception: out of memory",
"Should have the right error message");
// Make sure we finish async, in case the expectUncaughtException assertion
// about having seen the exception runs after our listener
SimpleTest.executeSoon(SimpleTest.finish);
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>
+1 -1
View File
@@ -78,6 +78,7 @@ UNIFIED_SOURCES += [
'CommandEvent.cpp',
'CompositionEvent.cpp',
'ContentEventHandler.cpp',
'CustomEvent.cpp',
'DataContainerEvent.cpp',
'DataTransfer.cpp',
'DeviceMotionEvent.cpp',
@@ -117,7 +118,6 @@ UNIFIED_SOURCES += [
# nsEventStateManager.cpp should be built separately because of Mac OS X headers.
SOURCES += [
'CustomEvent.cpp',
'EventStateManager.cpp',
]
+1 -27
View File
@@ -3170,17 +3170,7 @@ void HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags)
stream->AddAudioOutput(this);
SetVolumeInternal();
#ifdef MOZ_WIDGET_GONK
bool bUseOverlayImage = mSrcStream->AsDOMHwMediaStream() != nullptr;
#else
bool bUseOverlayImage = false;
#endif
VideoFrameContainer* container;
if (bUseOverlayImage) {
container = GetOverlayImageVideoFrameContainer();
} else {
container = GetVideoFrameContainer();
}
VideoFrameContainer* container = GetVideoFrameContainer();
if (container) {
stream->AddVideoOutput(container);
}
@@ -4045,22 +4035,6 @@ VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
return mVideoFrameContainer;
}
VideoFrameContainer* HTMLMediaElement::GetOverlayImageVideoFrameContainer()
{
if (mVideoFrameContainer)
return mVideoFrameContainer;
// Only video frames need an image container.
if (!IsVideo()) {
return nullptr;
}
mVideoFrameContainer =
new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY));
return mVideoFrameContainer;
}
nsresult HTMLMediaElement::DispatchEvent(const nsAString& aName)
{
LOG_EVENT(LogLevel::Debug, ("%p Dispatching event %s", this,
-3
View File
@@ -219,9 +219,6 @@ public:
virtual bool IsHidden() const final override;
// In order to create overlayImageContainer to support DOMHwMediaStream.
VideoFrameContainer* GetOverlayImageVideoFrameContainer();
// Called by the media decoder and the video frame to get the
// ImageContainer containing the video data.
B2G_ACL_EXPORT virtual VideoFrameContainer* GetVideoFrameContainer() final override;
+1 -1
View File
@@ -1043,7 +1043,7 @@ DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
DOMHwMediaStream::DOMHwMediaStream()
{
#ifdef MOZ_WIDGET_GONK
mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY);
mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
mOverlayImage = mImageContainer->CreateOverlayImage();
nsAutoTArray<ImageContainer::NonOwningImage,1> images;
images.AppendElement(ImageContainer::NonOwningImage(mOverlayImage));
+25 -54
View File
@@ -25,7 +25,8 @@
#include "nsIContent.h"
#include "nsPluginInstanceOwner.h"
#include "nsWrapperCacheInlines.h"
#include "js/HashTable.h"
#include "js/GCHashTable.h"
#include "js/TracingAPI.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/plugins/PluginAsyncSurrogate.h"
@@ -42,21 +43,33 @@ using namespace mozilla;
using mozilla::plugins::PluginScriptableObjectParent;
using mozilla::plugins::ParentNPObject;
struct JSObjWrapperHasher : public js::DefaultHasher<nsJSObjWrapperKey>
struct JSObjWrapperHasher
{
typedef nsJSObjWrapperKey Key;
typedef Key Lookup;
static uint32_t hash(const Lookup &l) {
return HashGeneric(l.mJSObj, l.mNpp);
return js::MovableCellHasher<JS::Heap<JSObject*>>::hash(l.mJSObj) ^
HashGeneric(l.mNpp);
}
static void rekey(Key &k, const Key& newKey) {
MOZ_ASSERT(k.mNpp == newKey.mNpp);
k.mJSObj = newKey.mJSObj;
static bool match(const Key& k, const Lookup &l) {
return js::MovableCellHasher<JS::Heap<JSObject*>>::match(k.mJSObj, l.mJSObj) &&
k.mNpp == l.mNpp;
}
};
namespace js {
template <>
struct GCPolicy<nsJSObjWrapper*> {
static void trace(JSTracer* trc, nsJSObjWrapper** wrapper, const char* name) {
MOZ_ASSERT(wrapper);
MOZ_ASSERT(*wrapper);
(*wrapper)->trace(trc);
}
};
} // namespace js
class NPObjWrapperHashEntry : public PLDHashEntryHdr
{
public:
@@ -72,10 +85,10 @@ public:
// when a plugin is torn down in case there's a leak in the plugin (we
// don't want to leak the world just because a plugin leaks an
// NPObject).
typedef js::HashMap<nsJSObjWrapperKey,
nsJSObjWrapper*,
JSObjWrapperHasher,
js::SystemAllocPolicy> JSObjWrapperTable;
typedef js::GCHashMap<nsJSObjWrapperKey,
nsJSObjWrapper*,
JSObjWrapperHasher,
js::SystemAllocPolicy> JSObjWrapperTable;
static JSObjWrapperTable sJSObjWrappers;
// Whether it's safe to iterate sJSObjWrappers. Set to true when sJSObjWrappers
@@ -279,20 +292,8 @@ OnWrapperDestroyed();
static void
TraceJSObjWrappers(JSTracer *trc, void *data)
{
if (!sJSObjWrappers.initialized()) {
return;
}
// Trace all JSObjects in the sJSObjWrappers table and rekey the entries if
// any of them moved.
for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
nsJSObjWrapperKey key = e.front().key();
JS::UnsafeTraceRoot(trc, &key.mJSObj, "sJSObjWrappers key object");
nsJSObjWrapper *wrapper = e.front().value();
JS::TraceEdge(trc, &wrapper->mJSObj, "sJSObjWrappers wrapper object");
if (key != e.front().key()) {
e.rekeyFront(key);
}
if (sJSObjWrappers.initialized()) {
sJSObjWrappers.trace(trc);
}
}
@@ -1080,33 +1081,6 @@ nsJSObjWrapper::NP_Construct(NPObject* npobj, const NPVariant* args,
return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result);
}
/*
* This function is called during minor GCs for each key in the sJSObjWrappers
* table that has been moved.
*
* Note that the wrapper may be dead at this point, and even the table may have
* been finalized if all wrappers have died.
*/
static void
JSObjWrapperKeyMarkCallback(JSTracer* trc, JSObject* obj, void* data) {
NPP npp = static_cast<NPP>(data);
MOZ_ASSERT(sJSObjWrappersAccessible);
if (!sJSObjWrappers.initialized())
return;
JSObject* prior = obj;
nsJSObjWrapperKey oldKey(prior, npp);
JSObjWrapperTable::Ptr p = sJSObjWrappers.lookup(oldKey);
if (!p)
return;
js::UnsafeTraceManuallyBarrieredEdge(trc, &obj, "sJSObjWrappers key object");
nsJSObjWrapperKey newKey(obj, npp);
sJSObjWrappers.rekeyIfMoved(oldKey, newKey);
}
// Look up or create an NPObject that wraps the JSObject obj.
// static
@@ -1199,9 +1173,6 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext* cx, JS::Handle<JSObject*> obj)
return nullptr;
}
// Add postbarrier for the hashtable key
JS_StoreObjectPostBarrierCallback(cx, JSObjWrapperKeyMarkCallback, obj, wrapper->mNpp);
return wrapper;
}
+10 -1
View File
@@ -10,6 +10,7 @@
#include "npapi.h"
#include "npruntime.h"
#include "PLDHashTable.h"
#include "js/RootingAPI.h"
class nsJSNPRuntime
{
@@ -33,7 +34,11 @@ public:
return !(*this == other);
}
JSObject * mJSObj;
void trace(JSTracer* trc) {
JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapperKey");
}
JS::Heap<JSObject*> mJSObj;
const NPP mNpp;
};
@@ -48,6 +53,10 @@ public:
JS::Handle<JSObject*> obj);
static bool HasOwnProperty(NPObject* npobj, NPIdentifier npid);
void trace(JSTracer* trc) {
JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapper");
}
protected:
explicit nsJSObjWrapper(NPP npp);
~nsJSObjWrapper();
+3 -3
View File
@@ -931,10 +931,10 @@ Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal,
// Step 6 and step 7.
JS::Rooted<JS::Value> getCapabilities(aCx,
JS::ObjectValue(*getCapabilitiesObj));
JS::Rooted<JS::Value> promiseVal(aCx);
JS::Rooted<JSObject*> promiseObj(aCx);
if (!JS::Construct(aCx, aConstructor,
JS::HandleValueArray(getCapabilities),
&promiseVal)) {
&promiseObj)) {
aRv.NoteJSContextException();
return;
}
@@ -959,7 +959,7 @@ Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal,
aCapability.mReject = v;
// Step 10.
aCapability.mPromise = promiseVal;
aCapability.mPromise.setObject(*promiseObj);
// Step 11 doesn't need anything, since the PromiseCapability was passed in.
}
+2 -2
View File
@@ -60,10 +60,10 @@ public:
private:
// `true` if an instance of `FlushRejections` is currently dispatched
// and has not been executed yet.
static ThreadLocal<bool> sDispatched;
static MOZ_THREAD_LOCAL(bool) sDispatched;
};
/* static */ ThreadLocal<bool>
/* static */ MOZ_THREAD_LOCAL(bool)
FlushRejections::sDispatched;
static Promise*
+1
View File
@@ -61,6 +61,7 @@ skip-if = os == 'linux'
# disabled on OS X for intermittent failures--bug-798848
skip-if = toolkit == 'cocoa'
[test_nodesFromRect.html]
[test_parsingMode.html]
[test_popup_blocker_chrome.xul]
[test_queryCaretRect.html]
[test_resize_move_windows.xul]
@@ -0,0 +1,67 @@
<!DOCTYPE HTML>
<html>
<head>
<title>CSSStyleSheet parsingMode test - bug 1230491</title>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
function run() {
const Cc = Components.classes;
const Ci = Components.interfaces;
const sss = Cc["@mozilla.org/content/style-sheet-service;1"]
.getService(Ci.nsIStyleSheetService);
const domutils = Cc["@mozilla.org/inspector/dom-utils;1"]
.getService(Ci.inIDOMUtils);
const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
const userUrl = encodeURI("data:text/css,body { color: seagreen; }");
utils.loadSheetUsingURIString(userUrl, sss.USER_SHEET);
const agentUrl = encodeURI("data:text/css,body { color: tomato; }");
utils.loadSheetUsingURIString(agentUrl, sss.AGENT_SHEET);
const authorUrl = "chrome://mochikit/content/tests/SimpleTest/test.css";
let results = [];
for (let sheet of domutils.getAllStyleSheets(document)) {
if (sheet.href === agentUrl) {
is(sheet.parsingMode, "agent", "agent sheet has expected mode");
results[sss.AGENT_SHEET] = 1;
} else if (sheet.href === userUrl) {
is(sheet.parsingMode, "user", "user sheet has expected mode");
results[sss.USER_SHEET] = 1;
} else if (sheet.href === authorUrl) {
is(sheet.parsingMode, "author",
"author sheet has expected mode");
results[sss.AUTHOR_SHEET] = 1;
} else if (sheet.href === "about:PreferenceStyleSheet") {
is(sheet.parsingMode, "agent",
"about:PreferenceStyleSheet has agent mode");
continue;
} else {
// Ignore sheets we don't care about.
continue;
}
// Check that re-parsing preserves the mode.
let mode = sheet.parsingMode;
domutils.parseStyleSheet(sheet, "body { color: chartreuse; }");
is(sheet.parsingMode, mode,
"check that re-parsing preserved mode " + mode);
}
ok(results[sss.AGENT_SHEET] && results[sss.USER_SHEET] &&
results[sss.AUTHOR_SHEET],
"all sheets seen");
SimpleTest.finish();
}
</script>
</head>
<body onload="run()">
<div> What? </div>
</body>
</html>
+8
View File
@@ -9,11 +9,19 @@
interface CSSRule;
enum CSSStyleSheetParsingMode {
"author",
"user",
"agent"
};
interface CSSStyleSheet : StyleSheet {
[Pure]
readonly attribute CSSRule? ownerRule;
[Throws]
readonly attribute CSSRuleList cssRules;
[ChromeOnly]
readonly attribute CSSStyleSheetParsingMode parsingMode;
[Throws]
unsigned long insertRule(DOMString rule, unsigned long index);
[Throws]
+2 -2
View File
@@ -1212,11 +1212,11 @@ EventRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
if (obj && JS_IsArrayBufferObject(obj)) {
// Use cached response if the arraybuffer has been transfered.
if (mProxy->mArrayBufferResponseWasTransferred) {
MOZ_ASSERT(JS_IsNeuteredArrayBufferObject(obj));
MOZ_ASSERT(JS_IsDetachedArrayBufferObject(obj));
mUseCachedArrayBufferResponse = true;
doClone = false;
} else {
MOZ_ASSERT(!JS_IsNeuteredArrayBufferObject(obj));
MOZ_ASSERT(!JS_IsDetachedArrayBufferObject(obj));
JS::AutoValueArray<1> argv(aCx);
argv[0].set(response);
obj = JS_NewArrayObject(aCx, argv);
+1 -1
View File
@@ -29,7 +29,7 @@ namespace gl {
StaticMutex GLLibraryEGL::sMutex;
GLLibraryEGL sEGLLibrary;
#ifdef MOZ_B2G
ThreadLocal<EGLContext> GLLibraryEGL::sCurrentContext;
MOZ_THREAD_LOCAL(EGLContext) GLLibraryEGL::sCurrentContext;
#endif
// should match the order of EGLExtensions, and be null-terminated.
+1 -1
View File
@@ -598,7 +598,7 @@ public:
}
private:
static ThreadLocal<EGLContext> sCurrentContext;
static MOZ_THREAD_LOCAL(EGLContext) sCurrentContext;
public:
#else
+3 -5
View File
@@ -24,16 +24,14 @@ using mozilla::gl::GLFeature;
using mozilla::gl::SkiaGLGlue;
using mozilla::gfx::DrawTarget;
static mozilla::ThreadLocal<GLContext*> sGLContext;
static MOZ_THREAD_LOCAL(GLContext*) sGLContext;
extern "C" {
static void SetStaticGLContext(GLContext* context)
{
if (!sGLContext.initialized()) {
mozilla::DebugOnly<bool> success = sGLContext.init();
MOZ_ASSERT(success);
}
mozilla::DebugOnly<bool> success = sGLContext.init();
MOZ_ASSERT(success);
sGLContext.set(context);
}
+32
View File
@@ -119,6 +119,7 @@ namespace layers {
struct Effect;
struct EffectChain;
class Image;
class ImageHostOverlay;
class Layer;
class TextureSource;
class DataTextureSource;
@@ -453,6 +454,12 @@ public:
// these methods properly.
virtual nsIWidget* GetWidget() const { return nullptr; }
virtual bool HasImageHostOverlays() { return false; }
virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) {}
virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) {}
/**
* Debug-build assertion that can be called to ensure code is running on the
* compositor thread.
@@ -551,6 +558,31 @@ size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
decomposedRectArrayT* aLayerRects,
decomposedRectArrayT* aTextureRects);
static inline bool
BlendOpIsMixBlendMode(gfx::CompositionOp aOp)
{
switch (aOp) {
case gfx::CompositionOp::OP_MULTIPLY:
case gfx::CompositionOp::OP_SCREEN:
case gfx::CompositionOp::OP_OVERLAY:
case gfx::CompositionOp::OP_DARKEN:
case gfx::CompositionOp::OP_LIGHTEN:
case gfx::CompositionOp::OP_COLOR_DODGE:
case gfx::CompositionOp::OP_COLOR_BURN:
case gfx::CompositionOp::OP_HARD_LIGHT:
case gfx::CompositionOp::OP_SOFT_LIGHT:
case gfx::CompositionOp::OP_DIFFERENCE:
case gfx::CompositionOp::OP_EXCLUSION:
case gfx::CompositionOp::OP_HUE:
case gfx::CompositionOp::OP_SATURATION:
case gfx::CompositionOp::OP_COLOR:
case gfx::CompositionOp::OP_LUMINOSITY:
return true;
default:
return false;
}
}
} // namespace layers
} // namespace mozilla
-1
View File
@@ -140,7 +140,6 @@ enum class CompositableType : uint8_t {
UNKNOWN,
CONTENT_TILED, // tiled painted layer
IMAGE, // image with single buffering
IMAGE_OVERLAY, // image without buffer
IMAGE_BRIDGE, // ImageBridge protocol
CONTENT_SINGLE, // painted layer interface, single buffering
CONTENT_DOUBLE, // painted layer interface, double buffering
+2 -12
View File
@@ -139,11 +139,6 @@ ImageContainer::ImageContainer(Mode flag)
mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE, this).take();
MOZ_ASSERT(mImageClient);
break;
case ASYNCHRONOUS_OVERLAY:
mIPDLChild = new ImageContainerChild(this);
mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY, this).take();
MOZ_ASSERT(mImageClient);
break;
default:
MOZ_ASSERT(false, "This flag is invalid.");
break;
@@ -184,13 +179,8 @@ RefPtr<OverlayImage>
ImageContainer::CreateOverlayImage()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (mImageClient && mImageClient->GetTextureInfo().mCompositableType != CompositableType::IMAGE_OVERLAY) {
// If this ImageContainer is async but the image type mismatch, fix it here
if (ImageBridgeChild::IsCreated()) {
ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(
CompositableType::IMAGE_OVERLAY, this).take();
}
if (!mImageClient || !mImageClient->AsImageClientSingle()) {
return nullptr;
}
return new OverlayImage();
}
+15 -1
View File
@@ -15,6 +15,7 @@
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
#include "mozilla/TimeStamp.h" // for TimeStamp
#include "mozilla/gfx/Point.h" // For IntSize
#include "mozilla/layers/GonkNativeHandle.h"
#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr, etc
@@ -349,7 +350,7 @@ class ImageContainer final : public SupportsWeakPtr<ImageContainer> {
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01, ASYNCHRONOUS_OVERLAY = 0x02 };
enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01 };
explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
@@ -845,6 +846,11 @@ public:
gfx::IntSize mSize;
};
struct SidebandStreamData {
GonkNativeHandle mStream;
gfx::IntSize mSize;
};
OverlayImage() : Image(nullptr, ImageFormat::OVERLAY_IMAGE) { mOverlayId = INVALID_OVERLAY; }
void SetData(const Data& aData)
@@ -853,13 +859,21 @@ public:
mSize = aData.mSize;
}
void SetData(const SidebandStreamData& aData)
{
mSidebandStream = aData.mStream;
mSize = aData.mSize;
}
already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; } ;
int32_t GetOverlayId() { return mOverlayId; }
const GonkNativeHandle& GetSidebandStream() { return mSidebandStream; }
gfx::IntSize GetSize() { return mSize; }
private:
int32_t mOverlayId;
GonkNativeHandle mSidebandStream;
gfx::IntSize mSize;
};
#endif
+9 -10
View File
@@ -10,9 +10,7 @@
#ifdef MOZ_WIDGET_GONK
#include <utils/RefBase.h>
#if ANDROID_VERSION >= 21
#include <utils/NativeHandle.h>
#endif
#include "mozilla/layers/GonkNativeHandle.h"
#endif
#include "mozilla/gfx/Point.h" // for IntPoint
@@ -116,13 +114,16 @@ struct LayerRenderState {
void SetOverlayId(const int32_t& aId)
{ mOverlayId = aId; }
void SetSidebandStream(const GonkNativeHandle& aStream)
{
mSidebandStream = aStream;
}
android::GraphicBuffer* GetGrallocBuffer() const
{ return mSurface.get(); }
#if ANDROID_VERSION >= 21
android::NativeHandle* GetSidebandStream() const
{ return mSidebandStream.get(); }
#endif
const GonkNativeHandle& GetSidebandStream()
{ return mSidebandStream; }
#endif
void SetOffset(const nsIntPoint& aOffset)
@@ -146,9 +147,7 @@ struct LayerRenderState {
// size of mSurface
gfx::IntSize mSize;
TextureHost* mTexture;
#if ANDROID_VERSION >= 21
android::sp<android::NativeHandle> mSidebandStream;
#endif
GonkNativeHandle mSidebandStream;
#endif
};
-8
View File
@@ -107,14 +107,6 @@ protected:
AutoLockImage autoLock(mContainer);
#ifdef MOZ_WIDGET_GONK
if (autoLock.HasImage() &&
autoLock.GetImage()->GetFormat() == ImageFormat::OVERLAY_IMAGE) {
mImageClientTypeContainer = CompositableType::IMAGE_OVERLAY;
return mImageClientTypeContainer;
}
#endif
mImageClientTypeContainer = autoLock.HasImage()
? CompositableType::IMAGE : CompositableType::UNKNOWN;
return mImageClientTypeContainer;
+16 -40
View File
@@ -55,11 +55,6 @@ ImageClient::CreateImageClient(CompositableType aCompositableHostType,
case CompositableType::UNKNOWN:
result = nullptr;
break;
#ifdef MOZ_WIDGET_GONK
case CompositableType::IMAGE_OVERLAY:
result = new ImageClientOverlay(aForwarder, aFlags);
break;
#endif
default:
MOZ_CRASH("GFX: unhandled program type image");
}
@@ -154,6 +149,22 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlag
for (auto& img : images) {
Image* image = img.mImage;
#ifdef MOZ_WIDGET_GONK
if (image->GetFormat() == ImageFormat::OVERLAY_IMAGE) {
OverlayImage* overlayImage = static_cast<OverlayImage*>(image);
OverlaySource source;
if (overlayImage->GetSidebandStream().IsValid()) {
source.handle() = OverlayHandle(overlayImage->GetSidebandStream());
} else {
source.handle() = OverlayHandle(overlayImage->GetOverlayId());
}
source.size() = overlayImage->GetSize();
GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect());
continue;
}
#endif
RefPtr<TextureClient> texture = image->GetTextureClient(this);
for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
@@ -316,40 +327,5 @@ ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlag
return true;
}
#ifdef MOZ_WIDGET_GONK
ImageClientOverlay::ImageClientOverlay(CompositableForwarder* aFwd,
TextureFlags aFlags)
: ImageClient(aFwd, aFlags, CompositableType::IMAGE_OVERLAY)
{
}
bool
ImageClientOverlay::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
{
AutoLockImage autoLock(aContainer);
Image *image = autoLock.GetImage();
if (!image) {
return false;
}
if (mLastUpdateGenerationCounter == (uint32_t)image->GetSerial()) {
return true;
}
mLastUpdateGenerationCounter = (uint32_t)image->GetSerial();
if (image->GetFormat() == ImageFormat::OVERLAY_IMAGE) {
OverlayImage* overlayImage = static_cast<OverlayImage*>(image);
uint32_t overlayId = overlayImage->GetOverlayId();
gfx::IntSize size = overlayImage->GetSize();
OverlaySource source;
source.handle() = OverlayHandle(overlayId);
source.size() = size;
GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect());
}
return true;
}
#endif
} // namespace layers
} // namespace mozilla
-22
View File
@@ -140,28 +140,6 @@ protected:
uint64_t mAsyncContainerID;
};
#ifdef MOZ_WIDGET_GONK
/**
* And ImageClient to handle opaque video stream.
* Such video stream does not upload new Image for each frame.
* Gecko have no way to get the buffer content from the Image, since the Image
* does not contain the real buffer.
* It need special hardware to display the Image
*/
class ImageClientOverlay : public ImageClient
{
public:
ImageClientOverlay(CompositableForwarder* aFwd,
TextureFlags aFlags);
virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags);
TextureInfo GetTextureInfo() const override
{
return TextureInfo(CompositableType::IMAGE_OVERLAY);
}
};
#endif
} // namespace layers
} // namespace mozilla
@@ -202,11 +202,6 @@ CompositableHost::Create(const TextureInfo& aTextureInfo)
case CompositableType::IMAGE:
result = new ImageHost(aTextureInfo);
break;
#ifdef MOZ_WIDGET_GONK
case CompositableType::IMAGE_OVERLAY:
result = new ImageHostOverlay(aTextureInfo);
break;
#endif
case CompositableType::CONTENT_SINGLE:
result = new ContentHostSingleBuffered(aTextureInfo);
break;
+94 -12
View File
@@ -132,6 +132,20 @@ ImageHost::RemoveTextureHost(TextureHost* aTexture)
}
}
void
ImageHost::UseOverlaySource(OverlaySource aOverlay,
const gfx::IntRect& aPictureRect)
{
if (ImageHostOverlay::IsValid(aOverlay)) {
if (!mImageHostOverlay) {
mImageHostOverlay = new ImageHostOverlay();
}
mImageHostOverlay->UseOverlaySource(aOverlay, aPictureRect);
} else {
mImageHostOverlay = nullptr;
}
}
static TimeStamp
GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias)
{
@@ -264,6 +278,21 @@ ImageHost::Composite(LayerComposite* aLayer,
// set the new compositor yet.
return;
}
if (mImageHostOverlay) {
mImageHostOverlay->Composite(GetCompositor(),
mFlashCounter,
aLayer,
aEffectChain,
aOpacity,
aTransform,
aFilter,
aClipRect,
aVisibleRegion);
mBias = BIAS_NONE;
return;
}
int imageIndex = ChooseImageIndex();
if (imageIndex < 0) {
return;
@@ -412,6 +441,9 @@ ImageHost::SetCompositor(Compositor* aCompositor)
img.mFrontBuffer->SetCompositor(aCompositor);
}
}
if (mImageHostOverlay) {
mImageHostOverlay->SetCompositor(aCompositor);
}
CompositableHost::SetCompositor(aCompositor);
}
@@ -428,6 +460,10 @@ ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
img.mFrontBuffer->PrintInfo(aStream, pfx.get());
AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
}
if (mImageHostOverlay) {
mImageHostOverlay->PrintInfo(aStream, aPrefix);
}
}
void
@@ -447,6 +483,10 @@ ImageHost::Dump(std::stringstream& aStream,
LayerRenderState
ImageHost::GetRenderState()
{
if (mImageHostOverlay) {
return mImageHostOverlay->GetRenderState();
}
TimedImage* img = ChooseImage();
if (img) {
return img->mFrontBuffer->GetRenderState();
@@ -457,6 +497,10 @@ ImageHost::GetRenderState()
already_AddRefed<gfx::DataSourceSurface>
ImageHost::GetAsSurface()
{
if (mImageHostOverlay) {
return nullptr;
}
TimedImage* img = ChooseImage();
if (img) {
return img->mFrontBuffer->GetAsSurface();
@@ -493,6 +537,10 @@ ImageHost::Unlock()
IntSize
ImageHost::GetImageSize() const
{
if (mImageHostOverlay) {
return mImageHostOverlay->GetImageSize();
}
const TimedImage* img = ChooseImage();
if (img) {
return IntSize(img->mPictureRect.width, img->mPictureRect.height);
@@ -534,18 +582,47 @@ ImageHost::SetImageContainer(ImageContainerParent* aImageContainer)
}
}
#ifdef MOZ_WIDGET_GONK
ImageHostOverlay::ImageHostOverlay(const TextureInfo& aTextureInfo)
: CompositableHost(aTextureInfo)
ImageHostOverlay::ImageHostOverlay()
{
MOZ_COUNT_CTOR(ImageHostOverlay);
}
ImageHostOverlay::~ImageHostOverlay()
{
if (mCompositor) {
mCompositor->RemoveImageHostOverlay(this);
}
MOZ_COUNT_DTOR(ImageHostOverlay);
}
/* static */ bool
ImageHostOverlay::IsValid(OverlaySource aOverlay)
{
if ((aOverlay.handle().type() == OverlayHandle::Tint32_t) &&
aOverlay.handle().get_int32_t() != INVALID_OVERLAY) {
return true;
} else if (aOverlay.handle().type() == OverlayHandle::TGonkNativeHandle) {
return true;
}
return false;
}
void
ImageHostOverlay::Composite(LayerComposite* aLayer,
ImageHostOverlay::SetCompositor(Compositor* aCompositor)
{
if (mCompositor && (mCompositor != aCompositor)) {
mCompositor->RemoveImageHostOverlay(this);
}
if (aCompositor) {
aCompositor->AddImageHostOverlay(this);
}
mCompositor = aCompositor;
}
void
ImageHostOverlay::Composite(Compositor* aCompositor,
uint32_t aFlashCounter,
LayerComposite* aLayer,
EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
@@ -553,12 +630,12 @@ ImageHostOverlay::Composite(LayerComposite* aLayer,
const gfx::Rect& aClipRect,
const nsIntRegion* aVisibleRegion)
{
if (!GetCompositor()) {
MOZ_ASSERT(mCompositor == aCompositor);
if (mOverlay.handle().type() == OverlayHandle::Tnull_t) {
return;
}
if (mOverlay.handle().type() == OverlayHandle::Tnull_t)
return;
Color hollow(0.0f, 0.0f, 0.0f, 0.0f);
aEffectChain.mPrimaryEffect = new EffectSolidColor(hollow);
aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(CompositionOp::OP_SOURCE);
@@ -569,18 +646,24 @@ ImageHostOverlay::Composite(LayerComposite* aLayer,
rect.SetRect(mPictureRect.x, mPictureRect.y,
mPictureRect.width, mPictureRect.height);
mCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
mCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE,
rect, aClipRect, aTransform, mFlashCounter);
aCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
aCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE,
rect, aClipRect, aTransform, aFlashCounter);
}
LayerRenderState
ImageHostOverlay::GetRenderState()
{
LayerRenderState state;
#ifdef MOZ_WIDGET_GONK
if (mOverlay.handle().type() == OverlayHandle::Tint32_t) {
state.SetOverlayId(mOverlay.handle().get_int32_t());
} else if (mOverlay.handle().type() == OverlayHandle::TGonkNativeHandle) {
state.SetSidebandStream(mOverlay.handle().get_GonkNativeHandle());
}
state.mSize.width = mPictureRect.Width();
state.mSize.height = mPictureRect.Height();
#endif
return state;
}
@@ -602,7 +685,7 @@ void
ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
aStream << aPrefix;
aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
aStream << nsPrintfCString("ImageHostOverlay (0x%p)", this).get();
AppendToString(aStream, mPictureRect, " [picture-rect=", "]");
@@ -613,6 +696,5 @@ ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
}
}
#endif
} // namespace layers
} // namespace mozilla
+24 -14
View File
@@ -30,6 +30,7 @@ namespace layers {
class Compositor;
struct EffectChain;
class ImageContainerParent;
class ImageHostOverlay;
/**
* ImageHost. Works with ImageClientSingle and ImageClientBuffered
@@ -54,6 +55,9 @@ public:
virtual void RemoveTextureHost(TextureHost* aTexture) override;
virtual void UseOverlaySource(OverlaySource aOverlay,
const gfx::IntRect& aPictureRect) override;
virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
virtual void Attach(Layer* aLayer,
@@ -137,39 +141,45 @@ protected:
Bias mBias;
bool mLocked;
RefPtr<ImageHostOverlay> mImageHostOverlay;
};
#ifdef MOZ_WIDGET_GONK
/**
* ImageHostOverlay works with ImageClientOverlay
* ImageHostOverlay handles OverlaySource compositing
*/
class ImageHostOverlay : public CompositableHost {
class ImageHostOverlay {
protected:
virtual ~ImageHostOverlay();
public:
ImageHostOverlay(const TextureInfo& aTextureInfo);
~ImageHostOverlay();
NS_INLINE_DECL_REFCOUNTING(ImageHostOverlay)
ImageHostOverlay();
virtual CompositableType GetType() { return mTextureInfo.mCompositableType; }
static bool IsValid(OverlaySource aOverlay);
virtual void Composite(LayerComposite* aLayer,
void SetCompositor(Compositor* aCompositor);
virtual void Composite(Compositor* aCompositor,
uint32_t aFlashCounter,
LayerComposite* aLayer,
EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Filter& aFilter,
const gfx::Rect& aClipRect,
const nsIntRegion* aVisibleRegion = nullptr) override;
virtual LayerRenderState GetRenderState() override;
const nsIntRegion* aVisibleRegion);
virtual LayerRenderState GetRenderState();
virtual void UseOverlaySource(OverlaySource aOverlay,
const gfx::IntRect& aPictureRect) override;
virtual gfx::IntSize GetImageSize() const override;
const gfx::IntRect& aPictureRect);
virtual gfx::IntSize GetImageSize() const;
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
protected:
RefPtr<Compositor> mCompositor;
gfx::IntRect mPictureRect;
OverlaySource mOverlay;
};
#endif
} // namespace layers
} // namespace mozilla
@@ -50,7 +50,6 @@ ImageLayerComposite::SetCompositableHost(CompositableHost* aHost)
{
switch (aHost->GetType()) {
case CompositableType::IMAGE:
case CompositableType::IMAGE_OVERLAY:
mImageHost = aHost;
return true;
default:
@@ -813,7 +813,9 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion)
if (!mTarget && !haveLayerEffects &&
gfxPrefs::Composer2DCompositionEnabled() &&
composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot,
mCompositor->GetWidget(), mGeometryChanged))
mCompositor->GetWidget(),
mGeometryChanged,
mCompositor->HasImageHostOverlays()))
{
LayerScope::SetHWComposed();
if (mFPS) {
@@ -217,7 +217,6 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
case CompositableOperation::TOpUseOverlaySource: {
const OpUseOverlaySource& op = aEdit.get_OpUseOverlaySource();
CompositableHost* compositable = AsCompositable(op);
MOZ_ASSERT(compositable->GetType() == CompositableType::IMAGE_OVERLAY, "Invalid operation!");
if (!ValidatePictureRect(op.overlay().size(), op.picture())) {
return false;
}
+67
View File
@@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
#include <unistd.h>
#include "GonkNativeHandle.h"
using namespace mozilla::layers;
namespace mozilla {
namespace layers {
GonkNativeHandle::GonkNativeHandle()
: mNhObj(new NhObj())
{
}
GonkNativeHandle::GonkNativeHandle(NhObj* aNhObj)
: mNhObj(aNhObj)
{
MOZ_ASSERT(aNhObj);
}
void
GonkNativeHandle::TransferToAnother(GonkNativeHandle& aHandle)
{
aHandle.mNhObj = this->GetAndResetNhObj();
}
already_AddRefed<GonkNativeHandle::NhObj>
GonkNativeHandle::GetAndResetNhObj()
{
RefPtr<NhObj> nhObj = mNhObj;
mNhObj = new NhObj();
return nhObj.forget();
}
already_AddRefed<GonkNativeHandle::NhObj>
GonkNativeHandle::GetDupNhObj()
{
RefPtr<NhObj> nhObj;
if (IsValid()) {
native_handle* nativeHandle =
native_handle_create(mNhObj->mHandle->numFds, mNhObj->mHandle->numInts);
for (int i = 0; i < mNhObj->mHandle->numFds; ++i) {
nativeHandle->data[i] = dup(mNhObj->mHandle->data[i]);
}
memcpy(nativeHandle->data + nativeHandle->numFds,
mNhObj->mHandle->data + mNhObj->mHandle->numFds,
sizeof(int) * mNhObj->mHandle->numInts);
nhObj = new GonkNativeHandle::NhObj(nativeHandle);
} else {
nhObj = new GonkNativeHandle::NhObj();
}
return nhObj.forget();
}
} // namespace layers
} // namespace mozilla
+95
View File
@@ -0,0 +1,95 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
#ifndef IPC_GonkNativeHandle_h
#define IPC_GonkNativeHandle_h
#ifdef MOZ_WIDGET_GONK
#include <cutils/native_handle.h>
#endif
#include "mozilla/RefPtr.h" // for RefPtr
#include "nsISupportsImpl.h"
namespace mozilla {
namespace layers {
#ifdef MOZ_WIDGET_GONK
// GonkNativeHandle wraps android's native_handle_t and is used to support
// android's sideband stream.
// The sideband stream is a device-specific mechanism for passing buffers
// to hwcomposer. It is used to render TV streams and DRM protected streams.
// The native_handle_t represents device-specific kernel objects on android.
class GonkNativeHandle {
public:
class NhObj {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NhObj)
friend class GonkNativeHandle;
public:
NhObj()
: mHandle(nullptr) {}
explicit NhObj(native_handle_t* aHandle)
: mHandle(aHandle) {}
native_handle_t* GetAndResetNativeHandle()
{
native_handle_t* handle = mHandle;
mHandle = nullptr;
return handle;
}
private:
virtual ~NhObj() {
if (mHandle) {
native_handle_close(mHandle);
native_handle_delete(mHandle);
}
}
native_handle_t* mHandle;
};
GonkNativeHandle();
explicit GonkNativeHandle(NhObj* aNhObj);
bool operator==(const GonkNativeHandle& aOther) const {
return mNhObj.get() == aOther.mNhObj.get();
}
bool IsValid() const
{
return mNhObj && mNhObj->mHandle;
}
void TransferToAnother(GonkNativeHandle& aHandle);
already_AddRefed<NhObj> GetAndResetNhObj();
already_AddRefed<NhObj> GetDupNhObj();
// Return non owning handle.
native_handle_t* GetRawNativeHandle() const
{
if (mNhObj) {
return mNhObj->mHandle;
}
return nullptr;
}
private:
RefPtr<NhObj> mNhObj;
};
#else
struct GonkNativeHandle {
bool operator==(const GonkNativeHandle&) const { return false; }
};
#endif
} // namespace layers
} // namespace mozilla
#endif // IPC_GonkNativeHandle_h
+60
View File
@@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
#include "GonkNativeHandleUtils.h"
using namespace mozilla::layers;
namespace IPC {
void
ParamTraits<GonkNativeHandle>::Write(Message* aMsg,
const paramType& aParam)
{
GonkNativeHandle handle = aParam;
MOZ_ASSERT(handle.IsValid());
RefPtr<GonkNativeHandle::NhObj> nhObj = handle.GetAndResetNhObj();
native_handle_t* nativeHandle = nhObj->GetAndResetNativeHandle();
aMsg->WriteSize(nativeHandle->numInts);
aMsg->WriteBytes((nativeHandle->data + nativeHandle->numFds), sizeof(int) * nativeHandle->numInts);
for (size_t i = 0; i < static_cast<size_t>(nativeHandle->numFds); ++i) {
aMsg->WriteFileDescriptor(base::FileDescriptor(nativeHandle->data[i], true));
}
}
bool
ParamTraits<GonkNativeHandle>::Read(const Message* aMsg,
void** aIter, paramType* aResult)
{
size_t numInts;
if (!aMsg->ReadSize(aIter, &numInts)) {
return false;
}
numInts /= sizeof(int);
size_t numFds = aMsg->num_fds();
native_handle* nativeHandle = native_handle_create(numFds, numInts);
const char* data = reinterpret_cast<const char*>(nativeHandle->data + nativeHandle->numFds);
if (!aMsg->ReadBytes(aIter, &data, numInts * sizeof(int))) {
return false;
}
for (size_t i = 0; i < static_cast<size_t>(nativeHandle->numFds); ++i) {
base::FileDescriptor fd;
if (!aMsg->ReadFileDescriptor(aIter, &fd)) {
return false;
}
nativeHandle->data[i] = fd.fd;
}
return true;
}
} // namespace IPC
+36
View File
@@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
#ifndef IPC_GonkNativeHandleUtils_h
#define IPC_GonkNativeHandleUtils_h
#include "ipc/IPCMessageUtils.h"
#include "GonkNativeHandle.h"
namespace IPC {
#ifdef MOZ_WIDGET_GONK
template <>
struct ParamTraits<mozilla::layers::GonkNativeHandle> {
typedef mozilla::layers::GonkNativeHandle paramType;
static void Write(Message* aMsg, const paramType& aParam);
static bool Read(const Message* aMsg, void** aIter, paramType* aResult);
};
#else
template <>
struct ParamTraits<mozilla::layers::GonkNativeHandle> {
typedef mozilla::layers::GonkNativeHandle paramType;
static void Write(Message*, const paramType&) {}
static bool Read(const Message*, void**, paramType*) { return false; }
};
#endif
} // namespace IPC
#endif // IPC_GonkNativeHandleUtils_h
+2
View File
@@ -14,12 +14,14 @@ using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
using gfxImageFormat from "gfxTypes.h";
using struct mozilla::layers::GonkNativeHandle from "mozilla/layers/GonkNativeHandleUtils.h";
namespace mozilla {
namespace layers {
union OverlayHandle {
int32_t;
GonkNativeHandle;
null_t;
};
+4
View File
@@ -153,6 +153,8 @@ EXPORTS.mozilla.layers += [
'ipc/CompositorLRU.h',
'ipc/CompositorParent.h',
'ipc/FenceUtils.h',
'ipc/GonkNativeHandle.h',
'ipc/GonkNativeHandleUtils.h',
'ipc/ImageBridgeChild.h',
'ipc/ImageBridgeParent.h',
'ipc/ImageContainerParent.h',
@@ -237,6 +239,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
]
]
SOURCES += [
'ipc/GonkNativeHandle.cpp',
'ipc/GonkNativeHandleUtils.cpp',
'ipc/ShadowLayerUtilsGralloc.cpp',
]
+4 -2
View File
@@ -49,8 +49,10 @@ public:
* Currently, when TryRender() returns true, the entire framebuffer
* must have been rendered.
*/
virtual bool TryRenderWithHwc(Layer* aRoot, nsIWidget* aWidget,
bool aGeometryChanged) = 0;
virtual bool TryRenderWithHwc(Layer* aRoot,
nsIWidget* aWidget,
bool aGeometryChanged,
bool aHasImageHostOverlays) = 0;
/**
* Return true if Composer2D does composition. Return false if Composer2D
+65 -24
View File
@@ -71,6 +71,18 @@ BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
aProgram->SetMaskLayerTransform(aTransform);
}
void
CompositorOGL::BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit)
{
MOZ_ASSERT(aBackdrop);
mGLContext->fActiveTexture(aTexUnit);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, aBackdrop);
mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
}
CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
int aSurfaceHeight, bool aUseExternalSurfaceSize)
: mWidget(aWidget)
@@ -690,9 +702,16 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
}
void
CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
GLuint aSourceFrameBuffer,
GLuint *aFBO, GLuint *aTexture)
{
*aTexture = CreateTexture(aRect, aCopyFromSource, aSourceFrameBuffer);
mGLContext->fGenFramebuffers(1, aFBO);
}
GLuint
CompositorOGL::CreateTexture(const IntRect& aRect, bool aCopyFromSource, GLuint aSourceFrameBuffer)
{
// we're about to create a framebuffer backed by textures to use as an intermediate
// surface. What to do if its size (as given by aRect) would exceed the
@@ -705,7 +724,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
clampedRect.width = std::min(clampedRect.width, maxTexSize);
clampedRect.height = std::min(clampedRect.height, maxTexSize);
GLuint tex, fbo;
GLuint tex;
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
mGLContext->fGenTextures(1, &tex);
@@ -757,6 +776,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
LOCAL_GL_UNSIGNED_BYTE,
buf.get());
}
GLenum error = mGLContext->fGetError();
if (error != LOCAL_GL_NO_ERROR) {
nsAutoCString msg;
@@ -784,10 +804,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
LOCAL_GL_CLAMP_TO_EDGE);
mGLContext->fBindTexture(mFBOTextureTarget, 0);
mGLContext->fGenFramebuffers(1, &fbo);
*aFBO = fbo;
*aTexture = tex;
return tex;
}
ShaderConfigOGL
@@ -838,6 +855,9 @@ CompositorOGL::GetShaderConfigFor(Effect *aEffect,
source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16);
config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
source->GetFormat());
if (!texturedEffect->mPremultiplied) {
config.SetNoPremultipliedAlpha();
}
break;
}
}
@@ -845,6 +865,7 @@ CompositorOGL::GetShaderConfigFor(Effect *aEffect,
config.SetMask2D(aMask == MaskType::Mask2d);
config.SetMask3D(aMask == MaskType::Mask3d);
config.SetDEAA(aDEAAEnabled);
config.SetCompositionOp(aOp);
return config;
}
@@ -881,10 +902,15 @@ CompositorOGL::ResetProgram()
mCurrentProgram = nullptr;
}
static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode, bool aIsPremultiplied = true)
{
if (BlendOpIsMixBlendMode(aBlendMode)) {
// Mix-blend modes require an extra step (or more) that cannot be expressed
// in the fixed-function blending capabilities of opengl. We handle them
// separately in shaders, and the shaders assume we will use our default
// blend function for compositing (premultiplied OP_OVER).
return false;
}
if (aBlendMode == gfx::CompositionOp::OP_OVER && aIsPremultiplied) {
return false;
}
@@ -1049,11 +1075,18 @@ CompositorOGL::DrawQuad(const Rect& aRect,
aOpacity = 1.f;
}
GLuint mixBlendBackdrop = 0;
gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
EffectBlendMode *blendEffect =
static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
blendMode = blendEffect->mBlendMode;
if (BlendOpIsMixBlendMode(blendMode)) {
gfx::IntRect rect(gfx::IntPoint(0, 0), mCurrentRenderTarget->GetSize());
mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
}
}
// Only apply DEAA to quads that have been transformed such that aliasing
@@ -1167,6 +1200,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE1);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
@@ -1204,6 +1240,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
}
BindAndDrawQuadWithTextureRect(program, aRect, texturedEffect->mTextureCoords, source);
}
@@ -1232,6 +1271,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
BindAndDrawQuadWithTextureRect(program,
aRect,
@@ -1266,6 +1308,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
BindAndDrawQuadWithTextureRect(program,
aRect,
@@ -1292,6 +1337,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
}
if (config.mFeatures & ENABLE_TEXTURE_RECT) {
// 2DRect case, get the multiplier right for a sampler2DRect
@@ -1375,6 +1423,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
}
if (mixBlendBackdrop) {
gl()->fDeleteTextures(1, &mixBlendBackdrop);
}
// in case rendering has used some other GL context
MakeCurrent();
@@ -1434,22 +1485,12 @@ CompositorOGL::EndFrame()
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
// Unbind all textures
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE1);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE2);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
for (GLuint i = 0; i <= 4; i++) {
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
}
}
+49
View File
@@ -34,6 +34,10 @@
#include "nscore.h" // for NS_IMETHOD
#include "gfxVR.h"
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
#include "nsTHashtable.h" // for nsTHashtable
#endif
class nsIWidget;
namespace mozilla {
@@ -48,6 +52,11 @@ class TextureSource;
struct Effect;
struct EffectChain;
class GLBlitTextureImageHelper;
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
class ImageHostOverlay;
#endif
/**
* Interface for pools of temporary gl textures for the compositor.
* The textures are fully owned by the pool, so the latter is responsible
@@ -207,6 +216,11 @@ public:
mFBOTextureTarget == LOCAL_GL_TEXTURE_2D,
SupportsPartialTextureUpdate());
result.mSupportedBlendModes += gfx::CompositionOp::OP_SOURCE;
for (uint8_t op = 0; op < uint8_t(gfx::CompositionOp::OP_COUNT); op++) {
if (BlendOpIsMixBlendMode(gfx::CompositionOp(op))) {
result.mSupportedBlendModes += gfx::CompositionOp(op);
}
}
return result;
}
@@ -268,6 +282,29 @@ public:
virtual nsIWidget* GetWidget() const override { return mWidget; }
virtual bool HasImageHostOverlays() override
{
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
return mImageHostOverlays.Count() > 0;
#else
return false;
#endif
}
virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) override
{
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
mImageHostOverlays.PutEntry(aOverlay);
#endif
}
virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) override
{
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
mImageHostOverlays.RemoveEntry(aOverlay);
#endif
}
GLContext* gl() const { return mGLContext; }
/**
* Clear the program state. This must be called
@@ -390,6 +427,7 @@ private:
void CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
GLuint aSourceFrameBuffer,
GLuint *aFBO, GLuint *aTexture);
GLuint CreateTexture(const gfx::IntRect& aRect, bool aCopyFromSource, GLuint aSourceFrameBuffer);
void BindAndDrawQuads(ShaderProgramOGL *aProg,
int aQuads,
@@ -413,6 +451,12 @@ private:
void ActivateProgram(ShaderProgramOGL *aProg);
void CleanupResources();
/**
* Bind the texture behind the current render target as the backdrop for a
* mix-blend shader.
*/
void BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit);
/**
* Copies the content of our backbuffer to the set transaction target.
* Does not restore the target FBO, so only call from EndFrame.
@@ -447,6 +491,11 @@ private:
gfx::Rect mRenderBound;
CompositorOGLVRObjects mVR;
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
nsTHashtable<nsPtrHashKey<ImageHostOverlay> > mImageHostOverlays;
#endif
};
} // namespace layers
+253 -5
View File
@@ -43,6 +43,7 @@ AddUniforms(ProgramProfileOGL& aProfile)
"uBlackTexture",
"uWhiteTexture",
"uMaskTexture",
"uBackdropTexture",
"uRenderColor",
"uTexCoordMultiplier",
"uCbCrTexCoordMultiplier",
@@ -148,9 +149,9 @@ ShaderConfigOGL::SetMask3D(bool aEnabled)
}
void
ShaderConfigOGL::SetPremultiply(bool aEnabled)
ShaderConfigOGL::SetNoPremultipliedAlpha()
{
SetFeature(ENABLE_PREMULTIPLY, aEnabled);
SetFeature(ENABLE_NO_PREMUL_ALPHA, true);
}
void
@@ -159,6 +160,12 @@ ShaderConfigOGL::SetDEAA(bool aEnabled)
SetFeature(ENABLE_DEAA, aEnabled);
}
void
ShaderConfigOGL::SetCompositionOp(CompositionOp aOp)
{
mCompositionOp = aOp;
}
/* static */ ProgramProfileOGL
ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
{
@@ -167,6 +174,8 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
AddUniforms(result);
CompositionOp blendOp = aConfig.mCompositionOp;
vs << "#ifdef GL_ES" << endl;
vs << "#define EDGE_PRECISION mediump" << endl;
vs << "#else" << endl;
@@ -190,6 +199,10 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
vs << "varying vec2 vTexCoord;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
vs << "varying vec2 vBackdropCoord;" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_2D ||
aConfig.mFeatures & ENABLE_MASK_3D) {
vs << "uniform mat4 uMaskTransform;" << endl;
@@ -273,6 +286,10 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
}
vs << " finalPosition.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
vs << " finalPosition = uMatrixProj * finalPosition;" << endl;
if (BlendOpIsMixBlendMode(blendOp)) {
// Move from clip space coordinates into texture/uv-coordinates.
vs << " vBackdropCoord = (finalPosition.xy + vec2(1.0, 1.0)) / 2.0;" << endl;
}
vs << " gl_Position = finalPosition;" << endl;
vs << "}" << endl;
@@ -309,6 +326,9 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl;
}
}
if (BlendOpIsMixBlendMode(blendOp)) {
fs << "varying vec2 vBackdropCoord;" << endl;
}
const char *sampler2D = "sampler2D";
const char *texture2D = "texture2D";
@@ -342,6 +362,13 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform " << sampler2D << " uTexture;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
// Component alpha should be flattened away inside blend containers.
MOZ_ASSERT(!(aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA));
fs << "uniform sampler2D uBackdropTexture;" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_2D ||
aConfig.mFeatures & ENABLE_MASK_3D) {
fs << "varying vec3 vMaskCoord;" << endl;
@@ -352,6 +379,10 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
BuildMixBlender(aConfig, fs);
}
if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
fs << "vec4 sample(vec2 coord) {" << endl;
fs << " vec4 color;" << endl;
@@ -459,9 +490,6 @@ For [0,1] instead of [0,255], and to 5 places:
if (aConfig.mFeatures & ENABLE_OPACITY) {
fs << " color *= uLayerOpacity;" << endl;
}
if (aConfig.mFeatures & ENABLE_PREMULTIPLY) {
fs << " color.rgb *= color.a;" << endl;
}
}
if (aConfig.mFeatures & ENABLE_DEAA) {
// Calculate the sub-pixel coverage of the pixel and modulate its opacity
@@ -473,6 +501,10 @@ For [0,1] instead of [0,255], and to 5 places:
fs << " deaaCoverage *= clamp(dot(uSSEdges[3], ssPos), 0.0, 1.0);" << endl;
fs << " color *= deaaCoverage;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
fs << " vec4 backdrop = texture2D(uBackdropTexture, vBackdropCoord);" << endl;
fs << " color = mixAndBlend(backdrop, color);" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_3D) {
fs << " vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl;
fs << " COLOR_PRECISION float mask = texture2D(uMaskTexture, maskCoords).r;" << endl;
@@ -507,10 +539,226 @@ For [0,1] instead of [0,255], and to 5 places:
aConfig.mFeatures & ENABLE_MASK_3D) {
result.mTextureCount = 1;
}
if (BlendOpIsMixBlendMode(blendOp)) {
result.mTextureCount += 1;
}
return result;
}
void
ProgramProfileOGL::BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs)
{
// From the "Compositing and Blending Level 1" spec.
// Generate helper functions first.
switch (aConfig.mCompositionOp) {
case gfx::CompositionOp::OP_OVERLAY:
case gfx::CompositionOp::OP_HARD_LIGHT:
// Note: we substitute (2*src-1) into the screen formula below.
fs << "float hardlight(float dest, float src) {" << endl;
fs << " if (src <= 0.5) {" << endl;
fs << " return dest * (2.0 * src);" << endl;
fs << " } else {" << endl;
fs << " return 2.0*dest + 2.0*src - 1.0 - 2.0*dest*src;" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_COLOR_DODGE:
fs << "float dodge(float dest, float src) {" << endl;
fs << " if (dest == 0.0) {" << endl;
fs << " return 0.0;" << endl;
fs << " } else if (src == 1.0) {" << endl;
fs << " return 1.0;" << endl;
fs << " } else {" << endl;
fs << " return min(1.0, dest / (1.0 - src));" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_COLOR_BURN:
fs << "float burn(float dest, float src) {" << endl;
fs << " if (dest == 1.0) {" << endl;
fs << " return 1.0;" << endl;
fs << " } else if (src == 0.0) {" << endl;
fs << " return 0.0;" << endl;
fs << " } else {" << endl;
fs << " return 1.0 - min(1.0, (1.0 - dest) / src);" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_SOFT_LIGHT:
fs << "float darken(float dest) {" << endl;
fs << " if (dest <= 0.25) {" << endl;
fs << " return ((16.0 * dest - 12.0) * dest + 4.0) * dest;" << endl;
fs << " } else {" << endl;
fs << " return sqrt(dest);" << endl;
fs << " }" << endl;
fs << "}" << endl;
fs << "float softlight(float dest, float src) {" << endl;
fs << " if (src <= 0.5) {" << endl;
fs << " return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);" << endl;
fs << " } else {" << endl;
fs << " return dest + (2.0 * src - 1.0) * (darken(dest) - dest);" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_HUE:
case gfx::CompositionOp::OP_SATURATION:
case gfx::CompositionOp::OP_COLOR:
case gfx::CompositionOp::OP_LUMINOSITY:
fs << "float Lum(vec3 c) {" << endl;
fs << " return dot(vec3(0.3, 0.59, 0.11), c);" << endl;
fs << "}" << endl;
fs << "vec3 ClipColor(vec3 c) {" << endl;
fs << " float L = Lum(c);" << endl;
fs << " float n = min(min(c.r, c.g), c.b);" << endl;
fs << " float x = max(max(c.r, c.g), c.b);" << endl;
fs << " if (n < 0.0) {" << endl;
fs << " c = L + (((c - L) * L) / (L - n));" << endl;
fs << " }" << endl;
fs << " if (x > 1.0) {" << endl;
fs << " c = L + (((c - L) * (1.0 - L)) / (x - L));" << endl;
fs << " }" << endl;
fs << " return c;" << endl;
fs << "}" << endl;
fs << "vec3 SetLum(vec3 c, float L) {" << endl;
fs << " float d = L - Lum(c);" << endl;
fs << " return ClipColor(vec3(" << endl;
fs << " c.r + d," << endl;
fs << " c.g + d," << endl;
fs << " c.b + d));" << endl;
fs << "}" << endl;
fs << "float Sat(vec3 c) {" << endl;
fs << " return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);" << endl;
fs << "}" << endl;
// To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
fs << "vec3 SetSatInner(vec3 c, float s) {" << endl;
fs << " if (c.b > c.r) {" << endl;
fs << " c.g = (((c.g - c.r) * s) / (c.b - c.r));" << endl;
fs << " c.b = s;" << endl;
fs << " } else {" << endl;
fs << " c.gb = vec2(0.0, 0.0);" << endl;
fs << " }" << endl;
fs << " return vec3(0.0, c.gb);" << endl;
fs << "}" << endl;
fs << "vec3 SetSat(vec3 c, float s) {" << endl;
fs << " if (c.r <= c.g) {" << endl;
fs << " if (c.g <= c.b) {" << endl;
fs << " c.rgb = SetSatInner(c.rgb, s);" << endl;
fs << " } else if (c.r <= c.b) {" << endl;
fs << " c.rbg = SetSatInner(c.rbg, s);" << endl;
fs << " } else {" << endl;
fs << " c.brg = SetSatInner(c.brg, s);" << endl;
fs << " }" << endl;
fs << " } else if (c.r <= c.b) {" << endl;
fs << " c.grb = SetSatInner(c.grb, s);" << endl;
fs << " } else if (c.g <= c.b) {" << endl;
fs << " c.gbr = SetSatInner(c.gbr, s);" << endl;
fs << " } else {" << endl;
fs << " c.bgr = SetSatInner(c.bgr, s);" << endl;
fs << " }" << endl;
fs << " return c;" << endl;
fs << "}" << endl;
break;
default:
break;
}
// Generate the main blending helper.
fs << "vec3 blend(vec3 dest, vec3 src) {" << endl;
switch (aConfig.mCompositionOp) {
case gfx::CompositionOp::OP_MULTIPLY:
fs << " return dest * src;" << endl;
break;
case gfx::CompositionOp::OP_SCREEN:
fs << " return dest + src - (dest * src);" << endl;
break;
case gfx::CompositionOp::OP_OVERLAY:
fs << " return vec3(" << endl;
fs << " hardlight(src.r, dest.r)," << endl;
fs << " hardlight(src.g, dest.g)," << endl;
fs << " hardlight(src.b, dest.b));" << endl;
break;
case gfx::CompositionOp::OP_DARKEN:
fs << " return min(dest, src);" << endl;
break;
case gfx::CompositionOp::OP_LIGHTEN:
fs << " return max(dest, src);" << endl;
break;
case gfx::CompositionOp::OP_COLOR_DODGE:
fs << " return vec3(" << endl;
fs << " dodge(dest.r, src.r)," << endl;
fs << " dodge(dest.g, src.g)," << endl;
fs << " dodge(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_COLOR_BURN:
fs << " return vec3(" << endl;
fs << " burn(dest.r, src.r)," << endl;
fs << " burn(dest.g, src.g)," << endl;
fs << " burn(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_HARD_LIGHT:
fs << " return vec3(" << endl;
fs << " hardlight(dest.r, src.r)," << endl;
fs << " hardlight(dest.g, src.g)," << endl;
fs << " hardlight(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_SOFT_LIGHT:
fs << " return vec3(" << endl;
fs << " softlight(dest.r, src.r)," << endl;
fs << " softlight(dest.g, src.g)," << endl;
fs << " softlight(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_DIFFERENCE:
fs << " return abs(dest - src);" << endl;
break;
case gfx::CompositionOp::OP_EXCLUSION:
fs << " return dest + src - 2.0*dest*src;" << endl;
break;
case gfx::CompositionOp::OP_HUE:
fs << " return SetLum(SetSat(src, Sat(dest)), Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_SATURATION:
fs << " return SetLum(SetSat(dest, Sat(src)), Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_COLOR:
fs << " return SetLum(src, Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_LUMINOSITY:
fs << " return SetLum(dest, Lum(src));" << endl;
break;
default:
MOZ_ASSERT_UNREACHABLE("unknown blend mode");
}
fs << "}" << endl;
// Generate the mix-blend function the fragment shader will call.
fs << "vec4 mixAndBlend(vec4 backdrop, vec4 color) {" << endl;
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// Infinity into the blend function and return incorrect results.
fs << " if (backdrop.a == 0.0) {" << endl;
fs << " return color;" << endl;
fs << " }" << endl;
fs << " if (color.a == 0.0) {" << endl;
fs << " return backdrop;" << endl;
fs << " }" << endl;
// The spec assumes there is no premultiplied alpha. The backdrop is always
// premultiplied, so undo the premultiply. If the source is premultiplied we
// must fix that as well.
fs << " backdrop.rgb /= backdrop.a;" << endl;
if (!(aConfig.mFeatures & ENABLE_NO_PREMUL_ALPHA)) {
fs << " color.rgb /= color.a;" << endl;
}
fs << " vec3 blended = blend(backdrop.rgb, color.rgb);" << endl;
fs << " color.rgb = (1.0 - backdrop.a) * color.rgb + backdrop.a * blended.rgb;" << endl;
fs << " color.rgb *= color.a;" << endl;
fs << " return color;" << endl;
fs << "}" << endl;
}
ShaderProgramOGL::ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile)
: mGL(aGL)
, mProgram(0)
+18 -4
View File
@@ -39,7 +39,7 @@ enum ShaderFeatures {
ENABLE_COLOR_MATRIX=0x400,
ENABLE_MASK_2D=0x800,
ENABLE_MASK_3D=0x1000,
ENABLE_PREMULTIPLY=0x2000,
ENABLE_NO_PREMUL_ALPHA=0x2000,
ENABLE_DEAA=0x4000
};
@@ -65,6 +65,7 @@ public:
BlackTexture,
WhiteTexture,
MaskTexture,
BackdropTexture,
RenderColor,
TexCoordMultiplier,
CbCrTexCoordMultiplier,
@@ -207,7 +208,9 @@ class ShaderConfigOGL
{
public:
ShaderConfigOGL() :
mFeatures(0) {}
mFeatures(0),
mCompositionOp(gfx::CompositionOp::OP_OVER)
{}
void SetRenderColor(bool aEnabled);
void SetTextureTarget(GLenum aTarget);
@@ -221,11 +224,14 @@ public:
void SetBlur(bool aEnabled);
void SetMask2D(bool aEnabled);
void SetMask3D(bool aEnabled);
void SetPremultiply(bool aEnabled);
void SetDEAA(bool aEnabled);
void SetCompositionOp(gfx::CompositionOp aOp);
void SetNoPremultipliedAlpha();
bool operator< (const ShaderConfigOGL& other) const {
return mFeatures < other.mFeatures;
return mFeatures < other.mFeatures ||
(mFeatures == other.mFeatures &&
(int)mCompositionOp < (int)other.mCompositionOp);
}
public:
@@ -237,6 +243,7 @@ public:
}
int mFeatures;
gfx::CompositionOp mCompositionOp;
};
static inline ShaderConfigOGL
@@ -278,6 +285,9 @@ struct ProgramProfileOGL
ProgramProfileOGL() :
mTextureCount(0)
{}
private:
static void BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs);
};
@@ -433,6 +443,10 @@ public:
SetUniform(KnownUniform::MaskTexture, aUnit);
}
void SetBackdropTextureUnit(GLint aUnit) {
SetUniform(KnownUniform::BackdropTexture, aUnit);
}
void SetRenderColor(const gfx::Color& aColor) {
SetUniform(KnownUniform::RenderColor, aColor);
}
+9 -7
View File
@@ -425,13 +425,15 @@ WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId,
ContextOptionsRef(cx).setDontReportUncaught(true);
HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
bool success;
if (construct)
success = JS::Construct(cx, vals[0], args, &rval);
else
success = JS::Call(cx, vals[1], vals[0], args, &rval);
if (!success)
return fail(aes, rs);
if (construct) {
RootedObject obj(cx);
if (!JS::Construct(cx, vals[0], args, &obj))
return fail(aes, rs);
rval.setObject(*obj);
} else {
if(!JS::Call(cx, vals[1], vals[0], args, &rval))
return fail(aes, rs);
}
}
if (!toVariant(cx, rval, result))
+1 -1
View File
@@ -176,7 +176,7 @@ LossyTwoByteCharsToNewLatin1CharsZ(js::ExclusiveContext* cx,
template <typename CharT>
extern UTF8CharsZ
CharsToNewUTF8CharsZ(js::ExclusiveContext* maybeCx, const mozilla::Range<const CharT> chars);
CharsToNewUTF8CharsZ(js::ExclusiveContext* maybeCx, const mozilla::Range<CharT> chars);
uint32_t
Utf8ToOneUcs4Char(const uint8_t* utf8Buffer, int utf8Length);
+34
View File
@@ -120,6 +120,12 @@ enum Reason {
NUM_REASONS,
};
/**
* Get a statically allocated C string explaining the given GC reason.
*/
extern JS_PUBLIC_API(const char*)
ExplainReason(JS::gcreason::Reason reason);
} /* namespace gcreason */
/*
@@ -343,6 +349,34 @@ typedef void
extern JS_PUBLIC_API(GCSliceCallback)
SetGCSliceCallback(JSRuntime* rt, GCSliceCallback callback);
/**
* Describes the progress of an observed nursery collection.
*/
enum class GCNurseryProgress {
/**
* The nursery collection is starting.
*/
GC_NURSERY_COLLECTION_START,
/**
* The nursery collection is ending.
*/
GC_NURSERY_COLLECTION_END
};
/**
* A nursery collection callback receives the progress of the nursery collection
* and the reason for the collection.
*/
using GCNurseryCollectionCallback = void(*)(JSRuntime* rt, GCNurseryProgress progress,
gcreason::Reason reason);
/**
* Set the nursery collection callback for the given runtime. When set, it will
* be called at the start and end of every nursery collection.
*/
extern JS_PUBLIC_API(GCNurseryCollectionCallback)
SetGCNurseryCollectionCallback(JSRuntime* rt, GCNurseryCollectionCallback callback);
/**
* Incremental GC defaults to enabled, but may be disabled for testing or in
* embeddings that have not yet implemented barriers on their native classes.
+68 -37
View File
@@ -7,6 +7,7 @@
#ifndef GCHashTable_h
#define GCHashTable_h
#include "js/GCPolicyAPI.h"
#include "js/HashTable.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
@@ -15,12 +16,9 @@ namespace js {
// Define a reasonable default GC policy for GC-aware Maps.
template <typename Key, typename Value>
struct DefaultMapGCPolicy {
using KeyPolicy = DefaultGCPolicy<Key>;
using ValuePolicy = DefaultGCPolicy<Value>;
struct DefaultMapSweepPolicy {
static bool needsSweep(Key* key, Value* value) {
return KeyPolicy::needsSweep(key) || ValuePolicy::needsSweep(value);
return GCPolicy<Key>::needsSweep(key) || GCPolicy<Value>::needsSweep(value);
}
};
@@ -34,11 +32,11 @@ struct DefaultMapGCPolicy {
// appropriately.
//
// Most types of GC pointers as keys and values can be traced with no extra
// infrastructure. For structs, the DefaultGCPolicy<T> will call a trace()
// method on the struct. For other structs and non-gc-pointer members, ensure
// that there is a specialization of DefaultGCPolicy<T> with an appropriate
// trace() static method available to handle the custom type. Generic helpers
// can be found in js/public/TracingAPI.h.
// infrastructure. For structs, the GCPolicy<T> will call a trace() method on
// the struct. For other structs and non-gc-pointer members, ensure that there
// is a specialization of GCPolicy<T> with an appropriate trace() static method
// available to handle the custom type. Generic helpers can be found in
// js/public/TracingAPI.h.
//
// Note that this HashMap only knows *how* to trace and sweep (and the tracing
// can handle keys that move), but it does not itself cause tracing or sweeping
@@ -50,7 +48,7 @@ template <typename Key,
typename Value,
typename HashPolicy = DefaultHasher<Key>,
typename AllocPolicy = TempAllocPolicy,
typename GCPolicy = DefaultMapGCPolicy<Key, Value>>
typename MapSweepPolicy = DefaultMapSweepPolicy<Key, Value>>
class GCHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
public JS::Traceable
{
@@ -64,8 +62,8 @@ class GCHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
if (!this->initialized())
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
GCPolicy::ValuePolicy::trace(trc, &e.front().value(), "hashmap value");
GCPolicy::KeyPolicy::trace(trc, &e.front().mutableKey(), "hashmap key");
GCPolicy<Value>::trace(trc, &e.front().value(), "hashmap value");
GCPolicy<Key>::trace(trc, &e.front().mutableKey(), "hashmap key");
}
}
@@ -74,7 +72,7 @@ class GCHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
if (GCPolicy::needsSweep(&e.front().mutableKey(), &e.front().value()))
if (MapSweepPolicy::needsSweep(&e.front().mutableKey(), &e.front().value()))
e.removeFront();
}
}
@@ -92,6 +90,40 @@ class GCHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
GCHashMap& operator=(const GCHashMap& hm) = delete;
};
// HashMap that supports rekeying.
template <typename Key,
typename Value,
typename HashPolicy = DefaultHasher<Key>,
typename AllocPolicy = TempAllocPolicy,
typename MapSweepPolicy = DefaultMapSweepPolicy<Key, Value>>
class GCRekeyableHashMap : public GCHashMap<Key, Value, HashPolicy, AllocPolicy, MapSweepPolicy>
{
using Base = GCHashMap<Key, Value, HashPolicy, AllocPolicy>;
public:
explicit GCRekeyableHashMap(AllocPolicy a = AllocPolicy()) : Base(a) {}
void sweep() {
if (!this->initialized())
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
Key key(e.front().key());
if (MapSweepPolicy::needsSweep(&key, &e.front().value()))
e.removeFront();
else if (!HashPolicy::match(key, e.front().key()))
e.rekeyFront(key);
}
}
// GCRekeyableHashMap is movable
GCRekeyableHashMap(GCRekeyableHashMap&& rhs) : Base(mozilla::Forward<GCRekeyableHashMap>(rhs)) {}
void operator=(GCRekeyableHashMap&& rhs) {
MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
Base::operator=(mozilla::Forward<GCRekeyableHashMap>(rhs));
}
};
template <typename Outer, typename... Args>
class GCHashMapOperations
{
@@ -184,8 +216,8 @@ class HandleBase<GCHashMap<A,B,C,D,E>>
//
// Most types of GC pointers can be traced with no extra infrastructure. For
// structs and non-gc-pointer members, ensure that there is a specialization of
// DefaultGCPolicy<T> with an appropriate trace method available to handle the
// custom type. Generic helpers can be found in js/public/TracingAPI.h.
// GCPolicy<T> with an appropriate trace method available to handle the custom
// type. Generic helpers can be found in js/public/TracingAPI.h.
//
// Note that although this HashSet's trace will deal correctly with moved
// elements, it does not itself know when to barrier or trace elements. To
@@ -193,8 +225,7 @@ class HandleBase<GCHashMap<A,B,C,D,E>>
// manually.
template <typename T,
typename HashPolicy = DefaultHasher<T>,
typename AllocPolicy = TempAllocPolicy,
typename GCPolicy = DefaultGCPolicy<T>>
typename AllocPolicy = TempAllocPolicy>
class GCHashSet : public HashSet<T, HashPolicy, AllocPolicy>,
public JS::Traceable
{
@@ -208,14 +239,14 @@ class GCHashSet : public HashSet<T, HashPolicy, AllocPolicy>,
if (!this->initialized())
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront())
GCPolicy::trace(trc, &e.mutableFront(), "hashset element");
GCPolicy<T>::trace(trc, &e.mutableFront(), "hashset element");
}
void sweep() {
if (!this->initialized())
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
if (GCPolicy::needsSweep(&e.mutableFront()))
if (GCPolicy<T>::needsSweep(&e.mutableFront()))
e.removeFront();
}
}
@@ -302,40 +333,40 @@ class MutableGCHashSetOperations
}
};
template <typename T, typename HP, typename AP, typename GP>
class RootedBase<GCHashSet<T, HP, AP, GP>>
: public MutableGCHashSetOperations<JS::Rooted<GCHashSet<T, HP, AP, GP>>, T, HP, AP, GP>
template <typename T, typename HP, typename AP>
class RootedBase<GCHashSet<T, HP, AP>>
: public MutableGCHashSetOperations<JS::Rooted<GCHashSet<T, HP, AP>>, T, HP, AP>
{
using Set = GCHashSet<T, HP, AP, GP>;
using Set = GCHashSet<T, HP, AP>;
friend class GCHashSetOperations<JS::Rooted<Set>, T, HP, AP, GP>;
friend class GCHashSetOperations<JS::Rooted<Set>, T, HP, AP>;
const Set& extract() const { return *static_cast<const JS::Rooted<Set>*>(this)->address(); }
friend class MutableGCHashSetOperations<JS::Rooted<Set>, T, HP, AP, GP>;
friend class MutableGCHashSetOperations<JS::Rooted<Set>, T, HP, AP>;
Set& extract() { return *static_cast<JS::Rooted<Set>*>(this)->address(); }
};
template <typename T, typename HP, typename AP, typename GP>
class MutableHandleBase<GCHashSet<T, HP, AP, GP>>
: public MutableGCHashSetOperations<JS::MutableHandle<GCHashSet<T, HP, AP, GP>>, T, HP, AP, GP>
template <typename T, typename HP, typename AP>
class MutableHandleBase<GCHashSet<T, HP, AP>>
: public MutableGCHashSetOperations<JS::MutableHandle<GCHashSet<T, HP, AP>>, T, HP, AP>
{
using Set = GCHashSet<T, HP, AP, GP>;
using Set = GCHashSet<T, HP, AP>;
friend class GCHashSetOperations<JS::MutableHandle<Set>, T, HP, AP, GP>;
friend class GCHashSetOperations<JS::MutableHandle<Set>, T, HP, AP>;
const Set& extract() const {
return *static_cast<const JS::MutableHandle<Set>*>(this)->address();
}
friend class MutableGCHashSetOperations<JS::MutableHandle<Set>, T, HP, AP, GP>;
friend class MutableGCHashSetOperations<JS::MutableHandle<Set>, T, HP, AP>;
Set& extract() { return *static_cast<JS::MutableHandle<Set>*>(this)->address(); }
};
template <typename T, typename HP, typename AP, typename GP>
class HandleBase<GCHashSet<T, HP, AP, GP>>
: public GCHashSetOperations<JS::Handle<GCHashSet<T, HP, AP, GP>>, T, HP, AP, GP>
template <typename T, typename HP, typename AP>
class HandleBase<GCHashSet<T, HP, AP>>
: public GCHashSetOperations<JS::Handle<GCHashSet<T, HP, AP>>, T, HP, AP>
{
using Set = GCHashSet<T, HP, AP, GP>;
friend class GCHashSetOperations<JS::Handle<Set>, T, HP, AP, GP>;
using Set = GCHashSet<T, HP, AP>;
friend class GCHashSetOperations<JS::Handle<Set>, T, HP, AP>;
const Set& extract() const { return *static_cast<const JS::Handle<Set>*>(this)->address(); }
};
+116
View File
@@ -0,0 +1,116 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
// GC Policy Mechanism
// A GCPolicy controls how the GC interacts with both direct pointers to GC
// things (e.g. JSObject* or JSString*), tagged and/or optional pointers to GC
// things (e.g. Value or jsid), and C++ aggregate types (e.g.
// JSPropertyDescriptor or GCHashMap).
//
// The GCPolicy provides at a minimum:
//
// static T initial()
// - Tells the GC how to construct an empty T.
//
// static void trace(JSTracer, T* tp, const char* name)
// - Tells the GC how to traverse the edge. In the case of an aggregate,
// describe how to trace the children.
//
// static bool needsSweep(T* tp)
// - Tells the GC how to determine if an edge is about to be finalized,
// and potentially updates the edge for moving GC if not. For
// aggregates, it determines the weakness semantics of storing the
// aggregate inside a weak container of some sort. For example, you
// might specialize a weak table's key type GCPolicy to describe
// when an entry should be kept during sweeping. This is the primary
// reason that GC-supporting weak containers can override the [sweep?]
// policy on a per-container basis.
#ifndef GCPolicyAPI_h
#define GCPolicyAPI_h
#include "js/TraceKind.h"
#include "js/TracingAPI.h"
class JSAtom;
class JSFunction;
class JSObject;
class JSScript;
class JSString;
namespace JS {
class Symbol;
}
namespace js {
// Defines a policy for aggregate types with non-GC, i.e. C storage. This
// policy dispatches to the underlying aggregate for GC interactions.
template <typename T>
struct StructGCPolicy
{
static T initial() {
return T();
}
static void trace(JSTracer* trc, T* tp, const char* name) {
tp->trace(trc);
}
static bool needsSweep(T* tp) {
return tp->needsSweep();
}
};
// The default GC policy attempts to defer to methods on the underlying type.
// Most C++ structures that contain a default constructor, a trace function and
// a sweep function will work out of the box with Rooted, Handle, GCVector,
// and GCHash{Set,Map}.
template <typename T> struct GCPolicy : public StructGCPolicy<T> {};
// This policy ignores any GC interaction, e.g. for non-GC types.
template <typename T>
struct IgnoreGCPolicy {
static T initial() { return T(); }
static void trace(JSTracer* trc, T* t, const char* name) {}
static bool needsSweep(T* v) { return false; }
};
template <> struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
template <> struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
template <typename T>
struct GCPointerPolicy
{
static T initial() { return nullptr; }
static void trace(JSTracer* trc, T* vp, const char* name) {
if (*vp)
js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
}
static void needsSweep(T* vp) {
return js::gc::EdgeNeedsSweep(vp);
}
};
template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
template <> struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
template <typename T>
struct GCPolicy<JS::Heap<T>>
{
static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
JS::TraceEdge(trc, thingp, name);
}
static bool needsSweep(JS::Heap<T>* thingp) {
return gc::EdgeNeedsSweep(thingp);
}
};
} // namespace js
#endif // GCPolicyAPI_h
+23 -25
View File
@@ -9,6 +9,7 @@
#include "mozilla/Vector.h"
#include "js/GCPolicyAPI.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
#include "js/Vector.h"
@@ -21,9 +22,9 @@ namespace js {
// the contents.
//
// Most types of GC pointers as keys and values can be traced with no extra
// infrastructure. For structs and non-gc-pointer members, ensure that there
// is a specialization of DefaultGCPolicy<T> with an appropriate trace method
// available to handle the custom type. Generic helpers can be found in
// infrastructure. For structs and non-gc-pointer members, ensure that there is
// a specialization of GCPolicy<T> with an appropriate trace method available
// to handle the custom type. Generic helpers can be found in
// js/public/TracingAPI.h.
//
// Note that although this Vector's trace will deal correctly with moved items,
@@ -31,8 +32,7 @@ namespace js {
// it must either be used with Rooted, or barriered and traced manually.
template <typename T,
size_t MinInlineCapacity = 0,
typename AllocPolicy = TempAllocPolicy,
typename GCPolicy = DefaultGCPolicy<T>>
typename AllocPolicy = TempAllocPolicy>
class GCVector : public JS::Traceable
{
mozilla::Vector<T, MinInlineCapacity, AllocPolicy> vector;
@@ -122,14 +122,14 @@ class GCVector : public JS::Traceable
void trace(JSTracer* trc) {
for (auto& elem : vector)
GCPolicy::trace(trc, &elem, "vector element");
GCPolicy<T>::trace(trc, &elem, "vector element");
}
};
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename GCPolicy>
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
class GCVectorOperations
{
using Vec = GCVector<T, Capacity, AllocPolicy, GCPolicy>;
using Vec = GCVector<T, Capacity, AllocPolicy>;
const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
public:
@@ -146,11 +146,11 @@ class GCVectorOperations
}
};
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename GCPolicy>
template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
class MutableGCVectorOperations
: public GCVectorOperations<Outer, T, Capacity, AllocPolicy, GCPolicy>
: public GCVectorOperations<Outer, T, Capacity, AllocPolicy>
{
using Vec = GCVector<T, Capacity, AllocPolicy, GCPolicy>;
using Vec = GCVector<T, Capacity, AllocPolicy>;
const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
Vec& vec() { return static_cast<Outer*>(this)->get(); }
@@ -213,26 +213,24 @@ class MutableGCVectorOperations
void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
};
template <typename T, size_t N, typename AP, typename GP>
class RootedBase<GCVector<T,N,AP,GP>>
: public MutableGCVectorOperations<JS::Rooted<GCVector<T,N,AP,GP>>, T,N,AP,GP>
template <typename T, size_t N, typename AP>
class RootedBase<GCVector<T,N,AP>>
: public MutableGCVectorOperations<JS::Rooted<GCVector<T,N,AP>>, T,N,AP>
{};
template <typename T, size_t N, typename AP, typename GP>
class MutableHandleBase<GCVector<T,N,AP,GP>>
: public MutableGCVectorOperations<JS::MutableHandle<GCVector<T,N,AP,GP>>,
T,N,AP,GP>
template <typename T, size_t N, typename AP>
class MutableHandleBase<GCVector<T,N,AP>>
: public MutableGCVectorOperations<JS::MutableHandle<GCVector<T,N,AP>>, T,N,AP>
{};
template <typename T, size_t N, typename AP, typename GP>
class HandleBase<GCVector<T,N,AP,GP>>
: public GCVectorOperations<JS::Handle<GCVector<T,N,AP,GP>>, T,N,AP,GP>
template <typename T, size_t N, typename AP>
class HandleBase<GCVector<T,N,AP>>
: public GCVectorOperations<JS::Handle<GCVector<T,N,AP>>, T,N,AP>
{};
template <typename T, size_t N, typename AP, typename GP>
class PersistentRootedBase<GCVector<T,N,AP,GP>>
: public MutableGCVectorOperations<JS::PersistentRooted<GCVector<T,N,AP,GP>>,
T,N,AP,GP>
template <typename T, size_t N, typename AP>
class PersistentRootedBase<GCVector<T,N,AP>>
: public MutableGCVectorOperations<JS::PersistentRooted<GCVector<T,N,AP>>, T,N,AP>
{};
} // namespace js
+13
View File
@@ -11,6 +11,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Casting.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#include "mozilla/Opaque.h"
@@ -655,6 +656,18 @@ struct DefaultHasher<float>
}
};
// A hash policy that compares C strings.
struct CStringHasher
{
typedef const char* Lookup;
static js::HashNumber hash(Lookup l) {
return mozilla::HashString(l);
}
static bool match(const char* key, Lookup lookup) {
return strcmp(key, lookup) == 0;
}
};
/*****************************************************************************/
// Both HashMap and HashSet are implemented by a single HashTable that is even
+3
View File
@@ -172,6 +172,9 @@ template <>
struct GCPolicy<jsid>
{
static jsid initial() { return JSID_VOID; }
static void trace(JSTracer* trc, jsid* idp, const char* name) {
js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
}
};
template <>
-1
View File
@@ -468,7 +468,6 @@ struct RuntimeSizes
macro(_, MallocHeap, object) \
macro(_, MallocHeap, atomsTable) \
macro(_, MallocHeap, contexts) \
macro(_, MallocHeap, dtoa) \
macro(_, MallocHeap, temporary) \
macro(_, MallocHeap, interpreterStack) \
macro(_, MallocHeap, mathCache) \
+4 -40
View File
@@ -17,6 +17,7 @@
#include "jspubtd.h"
#include "js/GCAPI.h"
#include "js/GCPolicyAPI.h"
#include "js/HeapAPI.h"
#include "js/TypeDecls.h"
#include "js/Utility.h"
@@ -108,11 +109,6 @@ template <typename T>
struct BarrierMethods {
};
template <typename T>
struct GCPolicy {
static T initial() { return T(); }
};
template <typename T>
class RootedBase {};
@@ -513,23 +509,6 @@ class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T>
namespace js {
/**
* By default, things should use the inheritance hierarchy to find their
* ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that
* Rooted<T> may be used without the class definition being available.
*/
template <typename T>
struct RootKind
{
static ThingRootKind rootKind() { return T::rootKind(); }
};
template <typename T>
struct RootKind<T*>
{
static ThingRootKind rootKind() { return T::rootKind(); }
};
template <typename T>
struct BarrierMethods<T*>
{
@@ -611,8 +590,6 @@ namespace JS {
// |static void trace(T*, JSTracer*)|
class Traceable
{
public:
static js::ThingRootKind rootKind() { return js::THING_ROOT_TRACEABLE; }
};
} /* namespace JS */
@@ -698,8 +675,7 @@ class MOZ_RAII Rooted : public js::RootedBase<T>
/* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
void registerWithRootLists(js::RootLists& roots) {
js::ThingRootKind kind = js::RootKind<T>::rootKind();
this->stack = &roots.stackRoots_[kind];
this->stack = &roots.stackRoots_[JS::MapTypeToRootKind<T>::kind];
this->prev = *stack;
*stack = reinterpret_cast<Rooted<void*>*>(this);
}
@@ -1005,27 +981,15 @@ template<typename T>
class PersistentRooted : public js::PersistentRootedBase<T>,
private mozilla::LinkedListElement<PersistentRooted<T>>
{
typedef mozilla::LinkedListElement<PersistentRooted<T>> ListBase;
using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>;
friend class mozilla::LinkedList<PersistentRooted>;
friend class mozilla::LinkedListElement<PersistentRooted>;
friend struct js::gc::PersistentRootedMarker<T>;
friend void js::gc::FinishPersistentRootedChains(js::RootLists&);
void registerWithRootLists(js::RootLists& roots) {
MOZ_ASSERT(!initialized());
js::ThingRootKind kind = js::RootKind<T>::rootKind();
JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
roots.heapRoots_[kind].insertBack(reinterpret_cast<JS::PersistentRooted<void*>*>(this));
// Until marking and destruction support the full set, we assert that
// we don't try to add any unsupported types.
MOZ_ASSERT(kind == js::THING_ROOT_OBJECT ||
kind == js::THING_ROOT_SCRIPT ||
kind == js::THING_ROOT_STRING ||
kind == js::THING_ROOT_ID ||
kind == js::THING_ROOT_VALUE ||
kind == js::THING_ROOT_TRACEABLE);
}
public:
+66 -2
View File
@@ -60,6 +60,16 @@ static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mas
static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
// When this header is imported inside SpiderMonkey, the class definitions are
// available and we can query those definitions to find the correct kind
// directly from the class hierarchy.
template <typename T>
struct MapTypeToTraceKind {
static const JS::TraceKind kind = T::TraceKind;
};
// When this header is used outside SpiderMonkey, the class definitions are not
// available, so the following table containing all public GC types is used.
#define JS_FOR_EACH_TRACEKIND(D) \
/* PrettyName TypeName AddToCCKind */ \
D(BaseShape, js::BaseShape, true) \
@@ -72,8 +82,7 @@ static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "ma
D(String, JSString, false) \
D(Symbol, JS::Symbol, false)
// Map from base trace type to the trace kind.
template <typename T> struct MapTypeToTraceKind {};
// Map from all public types to their trace kind.
#define JS_EXPAND_DEF(name, type, _) \
template <> struct MapTypeToTraceKind<type> { \
static const JS::TraceKind kind = JS::TraceKind::name; \
@@ -81,6 +90,61 @@ template <typename T> struct MapTypeToTraceKind {};
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
#undef JS_EXPAND_DEF
// RootKind is closely related to TraceKind. Whereas TraceKind's indices are
// laid out for convenient embedding as a pointer tag, the indicies of RootKind
// are designed for use as array keys via EnumeratedArray.
enum class RootKind : int8_t
{
// These map 1:1 with trace kinds.
BaseShape = 0,
JitCode,
LazyScript,
Object,
ObjectGroup,
Script,
Shape,
String,
Symbol,
// These tagged pointers are special-cased for performance.
Id,
Value,
// Everything else.
Traceable,
Limit
};
// Most RootKind correspond directly to a trace kind.
template <TraceKind traceKind> struct MapTraceKindToRootKind {};
#define JS_EXPAND_DEF(name, _0, _1) \
template <> struct MapTraceKindToRootKind<JS::TraceKind::name> { \
static const JS::RootKind kind = JS::RootKind::name; \
};
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF)
#undef JS_EXPAND_DEF
// Specify the RootKind for all types. Value and jsid map to special cases;
// pointer types we can derive directly from the TraceKind; everything else
// should go in the Traceable list and use GCPolicy<T>::trace for tracing.
template <typename T>
struct MapTypeToRootKind {
static const JS::RootKind kind = JS::RootKind::Traceable;
};
template <typename T>
struct MapTypeToRootKind<T*> {
static const JS::RootKind kind = \
JS::MapTraceKindToRootKind<JS::MapTypeToTraceKind<T>::kind>::kind;
};
template <> struct MapTypeToRootKind<JS::Value> {
static const JS::RootKind kind = JS::RootKind::Value;
};
template <> struct MapTypeToRootKind<jsid> {
static const JS::RootKind kind = JS::RootKind::Id;
};
template <> struct MapTypeToRootKind<JSFunction*> : public MapTypeToRootKind<JSObject*> {};
// Fortunately, few places in the system need to deal with fully abstract
// cells. In those places that do, we generally want to move to a layout
// templated function as soon as possible. This template wraps the upcast
-65
View File
@@ -347,71 +347,6 @@ template <typename T>
extern JS_PUBLIC_API(bool)
EdgeNeedsSweep(JS::Heap<T>* edgep);
} // namespace gc
// Automates static dispatch for GC interaction with TraceableContainers.
template <typename>
struct DefaultGCPolicy;
// This policy dispatches GC methods to a method on the type.
template <typename T>
struct StructGCPolicy {
static void trace(JSTracer* trc, T* t, const char* name) {
// This is the default GC policy for storing GC things in containers.
// If your build is failing here, it means you either need an
// implementation of DefaultGCPolicy<T> for your type or, if this is
// the right policy for you, your struct or container is missing a
// trace method.
t->trace(trc);
}
static bool needsSweep(T* t) {
return t->needsSweep();
}
};
// This policy ignores any GC interaction, e.g. for non-GC types.
template <typename T>
struct IgnoreGCPolicy {
static void trace(JSTracer* trc, T* t, const char* name) {}
static bool needsSweep(T* v) { return false; }
};
// The default policy when no other more specific policy fits (e.g. for a
// direct GC pointer), is to assume a struct type that implements the needed
// methods.
template <typename T>
struct DefaultGCPolicy : public StructGCPolicy<T> {};
template <>
struct DefaultGCPolicy<jsid>
{
static void trace(JSTracer* trc, jsid* idp, const char* name) {
js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
}
};
template <>
struct DefaultGCPolicy<JS::Value>
{
static void trace(JSTracer* trc, JS::Value* v, const char* name) {
js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
}
};
template <> struct DefaultGCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
template <> struct DefaultGCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
template <typename T>
struct DefaultGCPolicy<JS::Heap<T>>
{
static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
JS::TraceEdge(trc, thingp, name);
}
static bool needsSweep(JS::Heap<T>* thingp) {
return gc::EdgeNeedsSweep(thingp);
}
};
} // namespace js
#endif /* js_TracingAPI_h */
+1 -1
View File
@@ -88,7 +88,7 @@ namespace JS {
_(ArrayDoubleConversion) \
_(ArrayRange) \
_(ArraySeenNegativeIndex) \
_(TypedObjectNeutered) \
_(TypedObjectHasDetachedBuffer) \
_(TypedObjectArrayRange) \
_(AccessNotDense) \
_(AccessNotSimdObject) \
+3
View File
@@ -1703,6 +1703,9 @@ template <>
struct GCPolicy<JS::Value>
{
static JS::Value initial() { return JS::UndefinedValue(); }
static void trace(JSTracer* trc, JS::Value* v, const char* name) {
js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
}
};
template <>
+803 -1147
View File
File diff suppressed because it is too large Load Diff
-11
View File
@@ -85,17 +85,6 @@ IsValidAsmJSHeapLength(uint32_t length);
extern uint32_t
RoundUpToNextValidAsmJSHeapLength(uint32_t length);
// The assumed page size; dynamically checked in CompileAsmJS.
#ifdef _MIPS_ARCH_LOONGSON3A
static const size_t AsmJSPageSize = 16384;
#else
static const size_t AsmJSPageSize = 4096;
#endif
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
extern const size_t AsmJSMappedSize;
#endif
} // namespace js
#endif // asmjs_asmjs_h
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasm_h
#define wasm_h
#include "gc/Rooting.h"
namespace js {
namespace wasm {
// Add wasm testing JS functions to the given JS global object.
bool
DefineTestingFunctions(JSContext* cx, JS::HandleObject globalObj);
// Return whether WebAssembly can be compiled on this platform.
bool
HasCompilerSupport(ExclusiveContext* cx);
// The WebAssembly spec hard-codes the virtual page size to be 64KiB and limits
// forces the linear memory to always be a multiple of 64KiB.
static const unsigned PageSize = 64 * 1024;
// When signal handling is used for bounds checking, MappedSize bytes are
// reserved and the subrange [0, memory_size) is given readwrite permission.
// See also static asserts in MIRGenerator::foldableOffsetRange.
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
static const uint64_t Uint32Range = uint64_t(UINT32_MAX) + 1;
static const uint64_t MappedSize = 2 * Uint32Range + PageSize;
#endif
} // namespace wasm
} // namespace js
#endif // namespace wasm_h
+610 -475
View File
File diff suppressed because it is too large Load Diff
+14 -17
View File
@@ -95,7 +95,6 @@ FrameIterator::settle()
break;
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::Interrupt:
case CodeRange::Inline:
MOZ_CRASH("Should not encounter an exit during iteration");
}
@@ -106,7 +105,13 @@ FrameIterator::functionDisplayAtom() const
{
MOZ_ASSERT(!done());
const char* chars = module_->functionName(codeRange_->funcNameIndex());
UniqueChars owner;
const char* chars = module_->getFuncName(cx_, codeRange_->funcIndex(), &owner);
if (!chars) {
cx_->clearPendingException();
return cx_->names().empty;
}
JSAtom* atom = AtomizeUTF8Chars(cx_, chars, strlen(chars));
if (!atom) {
cx_->clearPendingException();
@@ -117,12 +122,10 @@ FrameIterator::functionDisplayAtom() const
}
unsigned
FrameIterator::computeLine(uint32_t* column) const
FrameIterator::lineOrBytecode() const
{
MOZ_ASSERT(!done());
if (column)
*column = callsite_->column();
return callsite_->line();
return callsite_->lineOrBytecode();
}
/*****************************************************************************/
@@ -188,7 +191,7 @@ PushRetAddr(MacroAssembler& masm)
// generated code.
static void
GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
ProfilingOffsets* offsets, Label* maybeEntry = nullptr)
ProfilingOffsets* offsets)
{
#if !defined (JS_CODEGEN_ARM)
Register scratch = ABIArgGenerator::NonArg_VolatileReg;
@@ -211,8 +214,6 @@ GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason
#endif
offsets->begin = masm.currentOffset();
if (maybeEntry)
masm.bind(maybeEntry);
PushRetAddr(masm);
MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - offsets->begin);
@@ -381,10 +382,10 @@ wasm::GenerateFunctionEpilogue(MacroAssembler& masm, unsigned framePushed, FuncO
void
wasm::GenerateExitPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
ProfilingOffsets* offsets, Label* maybeEntry)
ProfilingOffsets* offsets)
{
masm.haltingAlign(CodeAlignment);
GenerateProfilingPrologue(masm, framePushed, reason, offsets, maybeEntry);
GenerateProfilingPrologue(masm, framePushed, reason, offsets);
masm.setFramePushed(framePushed);
}
@@ -489,7 +490,6 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
break;
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::Interrupt:
case CodeRange::Inline:
MOZ_CRASH("Unexpected CodeRange kind");
}
@@ -541,8 +541,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
switch (codeRange->kind()) {
case CodeRange::Function:
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::Interrupt: {
case CodeRange::ImportInterpExit: {
// When the pc is inside the prologue/epilogue, the innermost
// call's AsmJSFrame is not complete and thus fp points to the the
// second-to-innermost call's AsmJSFrame. Since fp can only tell you
@@ -654,7 +653,6 @@ ProfilingFrameIterator::operator++()
case CodeRange::Function:
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::Interrupt:
case CodeRange::Inline:
stackAddress_ = callerFP_;
callerPC_ = ReturnAddressFromFP(callerFP_);
@@ -692,11 +690,10 @@ ProfilingFrameIterator::label() const
}
switch (codeRange_->kind()) {
case CodeRange::Function: return module_->profilingLabel(codeRange_->funcNameIndex());
case CodeRange::Function: return module_->profilingLabel(codeRange_->funcIndex());
case CodeRange::Entry: return "entry trampoline (in asm.js)";
case CodeRange::ImportJitExit: return importJitDescription;
case CodeRange::ImportInterpExit: return importInterpDescription;
case CodeRange::Interrupt: return nativeDescription;
case CodeRange::Inline: return "inline stub (in asm.js)";
}
+2 -2
View File
@@ -56,7 +56,7 @@ class FrameIterator
void operator++();
bool done() const { return !fp_; }
JSAtom* functionDisplayAtom() const;
unsigned computeLine(uint32_t* column) const;
unsigned lineOrBytecode() const;
};
// An ExitReason describes the possible reasons for leaving compiled wasm code
@@ -98,7 +98,7 @@ class ProfilingFrameIterator
// Prologue/epilogue code generation
void
GenerateExitPrologue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
ProfilingOffsets* offsets, jit::Label* maybeEntry = nullptr);
ProfilingOffsets* offsets);
void
GenerateExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
ProfilingOffsets* offsets);
+260 -151
View File
@@ -18,7 +18,6 @@
#include "asmjs/WasmGenerator.h"
#include "asmjs/AsmJS.h"
#include "asmjs/WasmStubs.h"
#include "jit/MacroAssembler-inl.h"
@@ -35,14 +34,12 @@ static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
: cx_(cx),
slowFuncs_(cx),
lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
slowFuncs_(cx),
numSigs_(0),
lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
alloc_(&lifo_),
masm_(MacroAssembler::AsmJSToken(), alloc_),
sigs_(cx),
funcEntryOffsets_(cx),
exportFuncIndices_(cx),
funcIndexToExport_(cx),
parallel_(false),
outstanding_(0),
@@ -105,69 +102,47 @@ ParallelCompilationEnabled(ExclusiveContext* cx)
}
bool
ModuleGenerator::init()
ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename, ModuleKind kind)
{
module_ = cx_->make_unique<ModuleData>();
if (!funcIndexToExport_.init())
return false;
module_ = MakeUnique<ModuleData>();
if (!module_)
return false;
module_->globalBytes = InitialGlobalDataBytes;
module_->compileArgs = CompileArgs(cx_);
module_->kind = kind;
module_->heapUsage = HeapUsage::None;
module_->filename = Move(filename);
link_ = cx_->make_unique<StaticLinkData>();
link_ = MakeUnique<StaticLinkData>();
if (!link_)
return false;
if (!sigs_.init() || !funcIndexToExport_.init())
exportMap_ = MakeUnique<ExportMap>();
if (!exportMap_)
return false;
uint32_t numTasks;
if (ParallelCompilationEnabled(cx_) &&
HelperThreadState().wasmCompilationInProgress.compareExchange(false, true))
{
#ifdef DEBUG
{
AutoLockHelperThreadState lock;
MOZ_ASSERT(!HelperThreadState().wasmFailed());
MOZ_ASSERT(HelperThreadState().wasmWorklist().empty());
MOZ_ASSERT(HelperThreadState().wasmFinishedList().empty());
// For asm.js, the Vectors in ModuleGeneratorData are max-sized reservations
// and will be initialized in a linear order via init* functions as the
// module is generated. For wasm, the Vectors are correctly-sized and
// already initialized.
shared_ = Move(shared);
if (kind == ModuleKind::Wasm) {
numSigs_ = shared_->sigs.length();
module_->numFuncs = shared_->funcSigs.length();
module_->globalBytes = AlignBytes(module_->globalBytes, sizeof(void*));
for (ModuleImportGeneratorData& import : shared_->imports) {
MOZ_ASSERT(!import.globalDataOffset);
import.globalDataOffset = module_->globalBytes;
module_->globalBytes += Module::SizeOfImportExit;
if (!addImport(*import.sig, import.globalDataOffset))
return false;
}
#endif
parallel_ = true;
numTasks = HelperThreadState().maxWasmCompilationThreads();
} else {
numTasks = 1;
}
if (!tasks_.initCapacity(numTasks))
return false;
JSRuntime* runtime = cx_->compartment()->runtimeFromAnyThread();
for (size_t i = 0; i < numTasks; i++)
tasks_.infallibleEmplaceBack(runtime, args(), COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
if (!freeTasks_.reserve(numTasks))
return false;
for (size_t i = 0; i < numTasks; i++)
freeTasks_.infallibleAppend(&tasks_[i]);
return true;
}
bool
ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
{
uint32_t globalBytes = module_->globalBytes;
uint32_t pad = ComputeByteAlignment(globalBytes, align);
if (UINT32_MAX - globalBytes < pad + bytes)
return false;
globalBytes += pad;
*globalDataOffset = globalBytes;
globalBytes += bytes;
module_->globalBytes = globalBytes;
return true;
}
@@ -210,10 +185,12 @@ ModuleGenerator::finishTask(IonCompileTask* task)
results.offsets().offsetBy(offsetInWhole);
// Record the non-profiling entry for whole-module linking later.
// Cannot simply append because funcIndex order is nonlinear.
if (func.index() >= funcEntryOffsets_.length()) {
if (!funcEntryOffsets_.resize(func.index() + 1))
return false;
}
MOZ_ASSERT(funcEntryOffsets_[func.index()] == 0);
funcEntryOffsets_[func.index()] = results.offsets().nonProfilingEntry;
// Merge the compiled results into the whole-module masm.
@@ -223,19 +200,13 @@ ModuleGenerator::finishTask(IonCompileTask* task)
MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
// Add the CodeRange for this function.
CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func.name());
if (!funcName)
return false;
uint32_t nameIndex = module_->funcNames.length();
if (!module_->funcNames.emplaceBack(Move(funcName)))
return false;
if (!module_->codeRanges.emplaceBack(nameIndex, func.line(), results.offsets()))
if (!module_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets()))
return false;
// Keep a record of slow functions for printing in the final console message.
unsigned totalTime = func.generateTime() + results.compileTime();
if (totalTime >= SlowFunction::msThreshold) {
if (!slowFuncs_.emplaceBack(func.name(), totalTime, func.line(), func.column()))
if (!slowFuncs_.emplaceBack(func.index(), totalTime, func.lineOrBytecode()))
return false;
}
@@ -243,53 +214,132 @@ ModuleGenerator::finishTask(IonCompileTask* task)
return true;
}
const LifoSig*
ModuleGenerator::newLifoSig(const MallocSig& sig)
bool
ModuleGenerator::addImport(const Sig& sig, uint32_t globalDataOffset)
{
SigSet::AddPtr p = sigs_.lookupForAdd(sig);
if (p)
return *p;
Sig copy;
if (!copy.clone(sig))
return false;
LifoSig* lifoSig = LifoSig::new_(lifo_, sig);
if (!lifoSig || !sigs_.add(p, lifoSig))
return nullptr;
return lifoSig;
return module_->imports.emplaceBack(Move(copy), globalDataOffset);
}
bool
ModuleGenerator::allocateGlobalVar(ValType type, uint32_t* globalDataOffset)
ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
{
uint32_t globalBytes = module_->globalBytes;
uint32_t pad = ComputeByteAlignment(globalBytes, align);
if (UINT32_MAX - globalBytes < pad + bytes)
return false;
globalBytes += pad;
*globalDataOffset = globalBytes;
globalBytes += bytes;
module_->globalBytes = globalBytes;
return true;
}
bool
ModuleGenerator::allocateGlobalVar(ValType type, bool isConst, uint32_t* index)
{
MOZ_ASSERT(!startedFuncDefs());
unsigned width = 0;
switch (type) {
case wasm::ValType::I32:
case wasm::ValType::F32:
case ValType::I32:
case ValType::F32:
width = 4;
break;
case wasm::ValType::I64:
case wasm::ValType::F64:
case ValType::I64:
case ValType::F64:
width = 8;
break;
case wasm::ValType::I32x4:
case wasm::ValType::F32x4:
case wasm::ValType::B32x4:
case ValType::I32x4:
case ValType::F32x4:
case ValType::B32x4:
width = 16;
break;
case ValType::Limit:
MOZ_CRASH("Limit");
break;
}
return allocateGlobalBytes(width, width, globalDataOffset);
uint32_t offset;
if (!allocateGlobalBytes(width, width, &offset))
return false;
*index = shared_->globals.length();
return shared_->globals.append(AsmJSGlobalVariable(ToExprType(type), offset, isConst));
}
void
ModuleGenerator::initHeapUsage(HeapUsage heapUsage)
{
MOZ_ASSERT(module_->heapUsage == HeapUsage::None);
module_->heapUsage = heapUsage;
}
bool
ModuleGenerator::declareImport(MallocSig&& sig, unsigned* index)
ModuleGenerator::usesHeap() const
{
static_assert(Module::SizeOfImportExit % sizeof(void*) == 0, "word aligned");
return UsesHeap(module_->heapUsage);
}
void
ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
{
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(sigIndex == numSigs_);
numSigs_++;
MOZ_ASSERT(shared_->sigs[sigIndex] == Sig());
shared_->sigs[sigIndex] = Move(sig);
}
const DeclaredSig&
ModuleGenerator::sig(uint32_t index) const
{
MOZ_ASSERT(index < numSigs_);
return shared_->sigs[index];
}
bool
ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
{
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(funcIndex == module_->numFuncs);
MOZ_ASSERT(!shared_->funcSigs[funcIndex]);
module_->numFuncs++;
shared_->funcSigs[funcIndex] = &shared_->sigs[sigIndex];
return true;
}
const DeclaredSig&
ModuleGenerator::funcSig(uint32_t funcIndex) const
{
MOZ_ASSERT(shared_->funcSigs[funcIndex]);
return *shared_->funcSigs[funcIndex];
}
bool
ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex)
{
uint32_t globalDataOffset;
if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
return false;
*index = unsigned(module_->imports.length());
return module_->imports.emplaceBack(Move(sig), globalDataOffset);
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(importIndex == module_->imports.length());
if (!addImport(sig(sigIndex), globalDataOffset))
return false;
ModuleImportGeneratorData& import = shared_->imports[importIndex];
MOZ_ASSERT(!import.sig);
import.sig = &shared_->sigs[sigIndex];
import.globalDataOffset = globalDataOffset;
return true;
}
uint32_t
@@ -298,16 +348,11 @@ ModuleGenerator::numImports() const
return module_->imports.length();
}
uint32_t
ModuleGenerator::importExitGlobalDataOffset(uint32_t index) const
const ModuleImportGeneratorData&
ModuleGenerator::import(uint32_t index) const
{
return module_->imports[index].exitGlobalDataOffset();
}
const MallocSig&
ModuleGenerator::importSig(uint32_t index) const
{
return module_->imports[index].sig();
MOZ_ASSERT(shared_->imports[index].sig);
return shared_->imports[index];
}
bool
@@ -321,27 +366,47 @@ ModuleGenerator::defineImport(uint32_t index, ProfilingOffsets interpExit, Profi
}
bool
ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* exportIndex)
ModuleGenerator::declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex)
{
if (!exportMap_->fieldNames.append(Move(fieldName)))
return false;
FuncIndexMap::AddPtr p = funcIndexToExport_.lookupForAdd(funcIndex);
if (p) {
*exportIndex = p->value();
return true;
if (exportIndex)
*exportIndex = p->value();
return exportMap_->fieldsToExports.append(p->value());
}
*exportIndex = module_->exports.length();
return funcIndexToExport_.add(p, funcIndex, *exportIndex) &&
module_->exports.append(Move(sig)) &&
exportFuncIndices_.append(funcIndex);
uint32_t newExportIndex = module_->exports.length();
MOZ_ASSERT(newExportIndex < MaxExports);
if (exportIndex)
*exportIndex = newExportIndex;
Sig copy;
if (!copy.clone(funcSig(funcIndex)))
return false;
return module_->exports.append(Move(copy)) &&
funcIndexToExport_.add(p, funcIndex, newExportIndex) &&
exportMap_->fieldsToExports.append(newExportIndex) &&
exportMap_->exportFuncIndices.append(funcIndex);
}
uint32_t
ModuleGenerator::exportFuncIndex(uint32_t index) const
{
return exportFuncIndices_[index];
return exportMap_->exportFuncIndices[index];
}
const MallocSig&
uint32_t
ModuleGenerator::exportEntryOffset(uint32_t index) const
{
return funcEntryOffsets_[exportMap_->exportFuncIndices[index]];
}
const Sig&
ModuleGenerator::exportSig(uint32_t index) const
{
return module_->exports[index].sig();
@@ -361,9 +426,61 @@ ModuleGenerator::defineExport(uint32_t index, Offsets offsets)
}
bool
ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
UniqueBytecode* recycled, FunctionGenerator* fg)
ModuleGenerator::addMemoryExport(UniqueChars fieldName)
{
return exportMap_->fieldNames.append(Move(fieldName)) &&
exportMap_->fieldsToExports.append(ExportMap::MemoryExport);
}
bool
ModuleGenerator::startFuncDefs()
{
MOZ_ASSERT(!startedFuncDefs());
threadView_ = MakeUnique<ModuleGeneratorThreadView>(*shared_);
if (!threadView_)
return false;
if (!funcIndexToExport_.init())
return false;
uint32_t numTasks;
if (ParallelCompilationEnabled(cx_) &&
HelperThreadState().wasmCompilationInProgress.compareExchange(false, true))
{
#ifdef DEBUG
{
AutoLockHelperThreadState lock;
MOZ_ASSERT(!HelperThreadState().wasmFailed());
MOZ_ASSERT(HelperThreadState().wasmWorklist().empty());
MOZ_ASSERT(HelperThreadState().wasmFinishedList().empty());
}
#endif
parallel_ = true;
numTasks = HelperThreadState().maxWasmCompilationThreads();
} else {
numTasks = 1;
}
if (!tasks_.initCapacity(numTasks))
return false;
JSRuntime* rt = cx_->compartment()->runtimeFromAnyThread();
for (size_t i = 0; i < numTasks; i++)
tasks_.infallibleEmplaceBack(rt, args(), *threadView_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
if (!freeTasks_.reserve(numTasks))
return false;
for (size_t i = 0; i < numTasks; i++)
freeTasks_.infallibleAppend(&tasks_[i]);
MOZ_ASSERT(startedFuncDefs());
return true;
}
bool
ModuleGenerator::startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg)
{
MOZ_ASSERT(startedFuncDefs());
MOZ_ASSERT(!activeFunc_);
MOZ_ASSERT(!finishedFuncs_);
@@ -372,11 +489,16 @@ ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
IonCompileTask* task = freeTasks_.popCopy();
task->reset(recycled);
task->reset(&fg->bytecode_);
if (fg->bytecode_) {
fg->bytecode_->clear();
} else {
fg->bytecode_ = MakeUnique<Bytecode>();
if (!fg->bytecode_)
return false;
}
fg->name_= name;
fg->line_ = line;
fg->column_ = column;
fg->lineOrBytecode_ = lineOrBytecode;
fg->m_ = this;
fg->task_ = task;
activeFunc_ = fg;
@@ -384,21 +506,19 @@ ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
}
bool
ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, UniqueBytecode bytecode,
unsigned generateTime, FunctionGenerator* fg)
ModuleGenerator::finishFuncDef(uint32_t funcIndex, unsigned generateTime, FunctionGenerator* fg)
{
MOZ_ASSERT(activeFunc_ == fg);
UniqueFuncBytecode func = cx_->make_unique<FuncBytecode>(fg->name_,
fg->line_,
fg->column_,
Move(fg->callSourceCoords_),
funcIndex,
sig,
Move(bytecode),
Move(fg->localVars_),
generateTime
);
UniqueFuncBytecode func =
js::MakeUnique<FuncBytecode>(funcIndex,
funcSig(funcIndex),
Move(fg->bytecode_),
Move(fg->locals_),
fg->lineOrBytecode_,
Move(fg->callSiteLineNums_),
generateTime,
module_->kind);
if (!func)
return false;
@@ -422,8 +542,9 @@ ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, UniqueByteco
}
bool
ModuleGenerator::finishFuncs()
ModuleGenerator::finishFuncDefs()
{
MOZ_ASSERT(startedFuncDefs());
MOZ_ASSERT(!activeFunc_);
MOZ_ASSERT(!finishedFuncs_);
@@ -504,46 +625,33 @@ ModuleGenerator::defineInlineStub(Offsets offsets)
return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
}
bool
ModuleGenerator::defineSyncInterruptStub(ProfilingOffsets offsets)
void
ModuleGenerator::defineInterruptExit(uint32_t offset)
{
MOZ_ASSERT(finishedFuncs_);
return module_->codeRanges.emplaceBack(CodeRange::Interrupt, offsets);
link_->pod.interruptOffset = offset;
}
void
ModuleGenerator::defineOutOfBoundsExit(uint32_t offset)
{
MOZ_ASSERT(finishedFuncs_);
link_->pod.outOfBoundsOffset = offset;
}
bool
ModuleGenerator::defineAsyncInterruptStub(Offsets offsets)
{
MOZ_ASSERT(finishedFuncs_);
link_->pod.interruptOffset = offsets.begin;
return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
}
bool
ModuleGenerator::defineOutOfBoundsStub(Offsets offsets)
{
MOZ_ASSERT(finishedFuncs_);
link_->pod.outOfBoundsOffset = offsets.begin;
return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
}
bool
ModuleGenerator::finish(HeapUsage heapUsage,
MutedErrorsBool mutedErrors,
CacheableChars filename,
CacheableTwoByteChars displayURL,
ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames,
UniqueModuleData* module,
UniqueStaticLinkData* linkData,
UniqueExportMap* exportMap,
SlowFunctionVector* slowFuncs)
{
MOZ_ASSERT(!activeFunc_);
MOZ_ASSERT(finishedFuncs_);
module_->heapUsage = heapUsage;
module_->mutedErrors = mutedErrors;
module_->filename = Move(filename);
module_->prettyFuncNames = Move(prettyFuncNames);
if (!GenerateStubs(*this, UsesHeap(heapUsage)))
if (!GenerateStubs(*this))
return false;
masm_.finish();
@@ -553,11 +661,11 @@ ModuleGenerator::finish(HeapUsage heapUsage,
// Start global data on a new page so JIT code may be given independent
// protection flags. Note assumption that global data starts right after
// code below.
module_->codeBytes = AlignBytes(masm_.bytesNeeded(), AsmJSPageSize);
module_->codeBytes = AlignBytes(masm_.bytesNeeded(), gc::SystemPageSize());
// Inflate the global bytes up to page size so that the total bytes are a
// page size (as required by the allocator functions).
module_->globalBytes = AlignBytes(module_->globalBytes, AsmJSPageSize);
module_->globalBytes = AlignBytes(module_->globalBytes, gc::SystemPageSize());
// Allocate the code (guarded by a UniquePtr until it is given to the Module).
module_->code = AllocateCode(cx_, module_->totalBytes());
@@ -646,6 +754,7 @@ ModuleGenerator::finish(HeapUsage heapUsage,
*module = Move(module_);
*linkData = Move(link_);
*exportMap = Move(exportMap_);
*slowFuncs = Move(slowFuncs_);
return true;
}
+151 -76
View File
@@ -31,106 +31,185 @@ class FunctionGenerator;
// A slow function describes a function that took longer than msThreshold to
// validate and compile.
struct SlowFunction
{
SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
: name(name), ms(ms), line(line), column(column)
SlowFunction(uint32_t index, unsigned ms, unsigned lineOrBytecode)
: index(index), ms(ms), lineOrBytecode(lineOrBytecode)
{}
static const unsigned msThreshold = 250;
PropertyName* name;
uint32_t index;
unsigned ms;
unsigned line;
unsigned column;
unsigned lineOrBytecode;
};
typedef Vector<SlowFunction> SlowFunctionVector;
// The ModuleGeneratorData holds all the state shared between the
// ModuleGenerator and ModuleGeneratorThreadView. The ModuleGeneratorData
// is encapsulated by ModuleGenerator/ModuleGeneratorThreadView classes which
// present a race-free interface to the code in each thread assuming any given
// element is initialized by the ModuleGenerator thread before an index to that
// element is written to Bytecode sent to a ModuleGeneratorThreadView thread.
// Once created, the Vectors are never resized.
struct ModuleImportGeneratorData
{
const DeclaredSig* sig;
uint32_t globalDataOffset;
ModuleImportGeneratorData() : sig(nullptr), globalDataOffset(0) {}
explicit ModuleImportGeneratorData(const DeclaredSig* sig) : sig(sig), globalDataOffset(0) {}
};
typedef Vector<ModuleImportGeneratorData, 0, SystemAllocPolicy> ModuleImportGeneratorDataVector;
struct AsmJSGlobalVariable
{
ExprType type;
unsigned globalDataOffset;
bool isConst;
AsmJSGlobalVariable(ExprType type, unsigned offset, bool isConst)
: type(type), globalDataOffset(offset), isConst(isConst)
{}
};
typedef Vector<AsmJSGlobalVariable, 0, SystemAllocPolicy> AsmJSGlobalVariableVector;
struct ModuleGeneratorData
{
DeclaredSigVector sigs;
DeclaredSigPtrVector funcSigs;
ModuleImportGeneratorDataVector imports;
AsmJSGlobalVariableVector globals;
};
typedef UniquePtr<ModuleGeneratorData> UniqueModuleGeneratorData;
// The ModuleGeneratorThreadView class presents a restricted, read-only view of
// the shared state needed by helper threads. There is only one
// ModuleGeneratorThreadView object owned by ModuleGenerator and referenced by
// all compile tasks.
class ModuleGeneratorThreadView
{
const ModuleGeneratorData& shared_;
public:
explicit ModuleGeneratorThreadView(const ModuleGeneratorData& shared)
: shared_(shared)
{}
const DeclaredSig& sig(uint32_t sigIndex) const {
return shared_.sigs[sigIndex];
}
const DeclaredSig& funcSig(uint32_t funcIndex) const {
MOZ_ASSERT(shared_.funcSigs[funcIndex]);
return *shared_.funcSigs[funcIndex];
}
const ModuleImportGeneratorData& import(uint32_t importIndex) const {
MOZ_ASSERT(shared_.imports[importIndex].sig);
return shared_.imports[importIndex];
}
const AsmJSGlobalVariable& globalVar(uint32_t globalIndex) const {
return shared_.globals[globalIndex];
}
};
// A ModuleGenerator encapsulates the creation of a wasm module. During the
// lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created
// and destroyed to compile the individual function bodies. After generating all
// functions, ModuleGenerator::finish() must be called to complete the
// compilation and extract the resulting wasm module.
class MOZ_STACK_CLASS ModuleGenerator
{
typedef Vector<uint32_t> FuncOffsetVector;
typedef Vector<uint32_t> FuncIndexVector;
typedef UniquePtr<ModuleGeneratorThreadView> UniqueModuleGeneratorThreadView;
typedef HashMap<uint32_t, uint32_t> FuncIndexMap;
struct SigHashPolicy
{
typedef const MallocSig& Lookup;
static HashNumber hash(Lookup l) { return l.hash(); }
static bool match(const LifoSig* lhs, Lookup rhs) { return *lhs == rhs; }
};
typedef HashSet<const LifoSig*, SigHashPolicy> SigSet;
ExclusiveContext* cx_;
ExclusiveContext* cx_;
jit::JitContext jcx_;
// Data handed back to the caller in finish()
UniqueModuleData module_;
UniqueStaticLinkData link_;
SlowFunctionVector slowFuncs_;
UniqueModuleData module_;
UniqueStaticLinkData link_;
UniqueExportMap exportMap_;
SlowFunctionVector slowFuncs_;
// Data scoped to the ModuleGenerator's lifetime
LifoAlloc lifo_;
jit::JitContext jcx_;
jit::TempAllocator alloc_;
jit::MacroAssembler masm_;
SigSet sigs_;
FuncOffsetVector funcEntryOffsets_;
FuncIndexVector exportFuncIndices_;
FuncIndexMap funcIndexToExport_;
UniqueModuleGeneratorData shared_;
uint32_t numSigs_;
LifoAlloc lifo_;
jit::TempAllocator alloc_;
jit::MacroAssembler masm_;
Uint32Vector funcEntryOffsets_;
FuncIndexMap funcIndexToExport_;
// Parallel compilation
bool parallel_;
uint32_t outstanding_;
Vector<IonCompileTask> tasks_;
Vector<IonCompileTask*> freeTasks_;
bool parallel_;
uint32_t outstanding_;
UniqueModuleGeneratorThreadView threadView_;
Vector<IonCompileTask> tasks_;
Vector<IonCompileTask*> freeTasks_;
// Assertions
DebugOnly<FunctionGenerator*> activeFunc_;
DebugOnly<bool> finishedFuncs_;
DebugOnly<FunctionGenerator*> activeFunc_;
DebugOnly<bool> finishedFuncs_;
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
bool finishOutstandingTask();
bool finishTask(IonCompileTask* task);
bool addImport(const Sig& sig, uint32_t globalDataOffset);
bool startedFuncDefs() const { return !!threadView_; }
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
public:
explicit ModuleGenerator(ExclusiveContext* cx);
~ModuleGenerator();
bool init();
bool init(UniqueModuleGeneratorData shared, UniqueChars filename, ModuleKind = ModuleKind::Wasm);
bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; }
CompileArgs args() const { return module_->compileArgs; }
jit::MacroAssembler& masm() { return masm_; }
const FuncOffsetVector& funcEntryOffsets() const { return funcEntryOffsets_; }
const LifoSig* newLifoSig(const MallocSig& sig);
// asm.js global variables:
bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index);
const AsmJSGlobalVariable& globalVar(unsigned index) const { return shared_->globals[index]; }
// Global data:
bool allocateGlobalVar(ValType type, uint32_t* globalDataOffset);
// Heap usage:
void initHeapUsage(HeapUsage heapUsage);
bool usesHeap() const;
// Signatures:
void initSig(uint32_t sigIndex, Sig&& sig);
uint32_t numSigs() const { return numSigs_; }
const DeclaredSig& sig(uint32_t sigIndex) const;
// Function declarations:
bool initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
uint32_t numFuncSigs() const { return module_->numFuncs; }
const DeclaredSig& funcSig(uint32_t funcIndex) const;
// Imports:
bool declareImport(MallocSig&& sig, uint32_t* index);
bool initImport(uint32_t importIndex, uint32_t sigIndex);
uint32_t numImports() const;
uint32_t importExitGlobalDataOffset(uint32_t index) const;
const MallocSig& importSig(uint32_t index) const;
const ModuleImportGeneratorData& import(uint32_t index) const;
bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
// Exports:
bool declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* exportIndex);
bool declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex = nullptr);
uint32_t numExports() const;
uint32_t exportFuncIndex(uint32_t index) const;
const MallocSig& exportSig(uint32_t index) const;
uint32_t exportEntryOffset(uint32_t index) const;
const Sig& exportSig(uint32_t index) const;
bool defineExport(uint32_t index, Offsets offsets);
bool addMemoryExport(UniqueChars fieldName);
// Functions:
bool startFunc(PropertyName* name, unsigned line, unsigned column, UniqueBytecode* recycled,
FunctionGenerator* fg);
bool finishFunc(uint32_t funcIndex, const LifoSig& sig, UniqueBytecode bytecode,
unsigned generateTime, FunctionGenerator* fg);
bool finishFuncs();
// Function definitions:
bool startFuncDefs();
bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg);
bool finishFuncDef(uint32_t funcIndex, unsigned generateTime, FunctionGenerator* fg);
bool finishFuncDefs();
// Function-pointer tables:
bool declareFuncPtrTable(uint32_t numElems, uint32_t* index);
@@ -139,19 +218,16 @@ class MOZ_STACK_CLASS ModuleGenerator
// Stubs:
bool defineInlineStub(Offsets offsets);
bool defineSyncInterruptStub(ProfilingOffsets offsets);
bool defineAsyncInterruptStub(Offsets offsets);
bool defineOutOfBoundsStub(Offsets offsets);
void defineInterruptExit(uint32_t offset);
void defineOutOfBoundsExit(uint32_t offset);
// Return a ModuleData object which may be used to construct a Module, the
// StaticLinkData required to call Module::staticallyLink, and the list of
// functions that took a long time to compile.
bool finish(HeapUsage heapUsage,
MutedErrorsBool mutedErrors,
CacheableChars filename,
CacheableTwoByteChars displayURL,
bool finish(CacheableCharsVector&& prettyFuncNames,
UniqueModuleData* module,
UniqueStaticLinkData* staticLinkData,
UniqueExportMap* exportMap,
SlowFunctionVector* slowFuncs);
};
@@ -160,6 +236,7 @@ class MOZ_STACK_CLASS ModuleGenerator
// anything else. After the body is complete, ModuleGenerator::finishFunc must
// be called before the FunctionGenerator is destroyed and the next function is
// started.
class MOZ_STACK_CLASS FunctionGenerator
{
friend class ModuleGenerator;
@@ -167,32 +244,30 @@ class MOZ_STACK_CLASS FunctionGenerator
ModuleGenerator* m_;
IonCompileTask* task_;
// Function metadata created during function generation, then handed over
// to the FuncBytecode in ModuleGenerator::finishFunc().
SourceCoordsVector callSourceCoords_;
ValTypeVector localVars_;
// Data created during function generation, then handed over to the
// FuncBytecode in ModuleGenerator::finishFunc().
UniqueBytecode bytecode_;
Uint32Vector callSiteLineNums_;
ValTypeVector locals_;
// Note: this unrooted field assumes AutoKeepAtoms via TokenStream via
// asm.js compilation.
PropertyName* name_;
unsigned line_;
unsigned column_;
uint32_t lineOrBytecode_;
public:
FunctionGenerator()
: m_(nullptr),
task_(nullptr),
name_(nullptr),
line_(0),
column_(0)
: m_(nullptr), task_(nullptr), lineOrBytecode_(0)
{}
bool addSourceCoords(size_t byteOffset, uint32_t line, uint32_t column) {
SourceCoords sc = { byteOffset, line, column };
return callSourceCoords_.append(sc);
Bytecode& bytecode() const {
return *bytecode_;
}
bool addVariable(ValType v) {
return localVars_.append(v);
bool addCallSiteLineNum(uint32_t lineno) {
return callSiteLineNums_.append(lineno);
}
bool addLocal(ValType v) {
return locals_.append(v);
}
const ValTypeVector& locals() const {
return locals_;
}
};
File diff suppressed because it is too large Load Diff
+14 -2
View File
@@ -25,6 +25,12 @@
namespace js {
namespace wasm {
class ModuleGeneratorThreadView;
typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
// The FuncCompileResults contains the results of compiling a single function
// body, ready to be merged into the whole-module MacroAssembler.
class FuncCompileResults
@@ -62,6 +68,7 @@ class IonCompileTask
{
JSRuntime* const runtime_;
const CompileArgs args_;
ModuleGeneratorThreadView& mg_;
LifoAlloc lifo_;
UniqueFuncBytecode func_;
mozilla::Maybe<FuncCompileResults> results_;
@@ -70,9 +77,11 @@ class IonCompileTask
IonCompileTask& operator=(const IonCompileTask&) = delete;
public:
IonCompileTask(JSRuntime* runtime, CompileArgs args, size_t defaultChunkSize)
: runtime_(runtime),
IonCompileTask(JSRuntime* rt, CompileArgs args, ModuleGeneratorThreadView& mg,
size_t defaultChunkSize)
: runtime_(rt),
args_(args),
mg_(mg),
lifo_(defaultChunkSize),
func_(nullptr)
{}
@@ -85,6 +94,9 @@ class IonCompileTask
CompileArgs args() const {
return args_;
}
ModuleGeneratorThreadView& mg() const {
return mg_;
}
void init(UniqueFuncBytecode func) {
MOZ_ASSERT(!func_);
func_ = mozilla::Move(func);
+290 -227
View File
@@ -24,7 +24,6 @@
#include "jsprf.h"
#include "asmjs/AsmJS.h"
#include "asmjs/WasmSerialize.h"
#include "builtin/AtomicsObject.h"
#include "builtin/SIMD.h"
@@ -55,6 +54,8 @@ using mozilla::PodZero;
using mozilla::Swap;
using JS::GenericNaN;
const uint32_t ExportMap::MemoryExport;
UniqueCodePtr
wasm::AllocateCode(ExclusiveContext* cx, size_t bytes)
{
@@ -64,12 +65,10 @@ wasm::AllocateCode(ExclusiveContext* cx, size_t bytes)
unsigned permissions =
ExecutableAllocator::initialProtectionFlags(ExecutableAllocator::Writable);
void* p = AllocateExecutableMemory(nullptr, bytes, permissions, "asm-js-code", AsmJSPageSize);
void* p = AllocateExecutableMemory(nullptr, bytes, permissions, "asm-js-code", gc::SystemPageSize());
if (!p)
ReportOutOfMemory(cx);
MOZ_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
return UniqueCodePtr((uint8_t*)p, CodeDeleter(bytes));
}
@@ -77,7 +76,7 @@ void
CodeDeleter::operator()(uint8_t* p)
{
MOZ_ASSERT(bytes_ != 0);
DeallocateExecutableMemory(p, bytes_, AsmJSPageSize);
DeallocateExecutableMemory(p, bytes_, gc::SystemPageSize());
}
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
@@ -243,14 +242,14 @@ StaticLinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
}
static size_t
SerializedSigSize(const MallocSig& sig)
SerializedSigSize(const Sig& sig)
{
return sizeof(ExprType) +
SerializedPodVectorSize(sig.args());
}
static uint8_t*
SerializeSig(uint8_t* cursor, const MallocSig& sig)
SerializeSig(uint8_t* cursor, const Sig& sig)
{
cursor = WriteScalar<ExprType>(cursor, sig.ret());
cursor = SerializePodVector(cursor, sig.args());
@@ -258,33 +257,22 @@ SerializeSig(uint8_t* cursor, const MallocSig& sig)
}
static const uint8_t*
DeserializeSig(ExclusiveContext* cx, const uint8_t* cursor, MallocSig* sig)
DeserializeSig(ExclusiveContext* cx, const uint8_t* cursor, Sig* sig)
{
ExprType ret;
cursor = ReadScalar<ExprType>(cursor, &ret);
MallocSig::ArgVector args;
ValTypeVector args;
cursor = DeserializePodVector(cx, cursor, &args);
if (!cursor)
return nullptr;
sig->init(Move(args), ret);
*sig = Sig(Move(args), ret);
return cursor;
}
static bool
CloneSig(JSContext* cx, const MallocSig& sig, MallocSig* out)
{
MallocSig::ArgVector args;
if (!ClonePodVector(cx, sig.args(), &args))
return false;
out->init(Move(args), sig.ret());
return true;
}
static size_t
SizeOfSigExcludingThis(const MallocSig& sig, MallocSizeOf mallocSizeOf)
SizeOfSigExcludingThis(const Sig& sig, MallocSizeOf mallocSizeOf)
{
return sig.args().sizeOfExcludingThis(mallocSizeOf);
}
@@ -316,7 +304,7 @@ bool
Export::clone(JSContext* cx, Export* out) const
{
out->pod = pod;
return CloneSig(cx, sig_, &out->sig_);
return out->sig_.clone(sig_);
}
size_t
@@ -352,7 +340,7 @@ bool
Import::clone(JSContext* cx, Import* out) const
{
out->pod = pod;
return CloneSig(cx, sig_, &out->sig_);
return out->sig_.clone(sig_);
}
size_t
@@ -362,8 +350,8 @@ Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
}
CodeRange::CodeRange(Kind kind, Offsets offsets)
: nameIndex_(0),
lineNumber_(0),
: funcIndex_(0),
funcLineOrBytecode_(0),
begin_(offsets.begin),
profilingReturn_(0),
end_(offsets.end)
@@ -376,8 +364,8 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
}
CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
: nameIndex_(0),
lineNumber_(0),
: funcIndex_(0),
funcLineOrBytecode_(0),
begin_(offsets.begin),
profilingReturn_(offsets.profilingReturn),
end_(offsets.end)
@@ -387,12 +375,12 @@ CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
MOZ_ASSERT(begin_ < profilingReturn_);
MOZ_ASSERT(profilingReturn_ < end_);
MOZ_ASSERT(u.kind_ == ImportJitExit || u.kind_ == ImportInterpExit || u.kind_ == Interrupt);
MOZ_ASSERT(u.kind_ == ImportJitExit || u.kind_ == ImportInterpExit);
}
CodeRange::CodeRange(uint32_t nameIndex, uint32_t lineNumber, FuncOffsets offsets)
: nameIndex_(nameIndex),
lineNumber_(lineNumber)
CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
: funcIndex_(funcIndex),
funcLineOrBytecode_(funcLineOrBytecode)
{
PodZero(&u); // zero padding for Valgrind
u.kind_ = Function;
@@ -412,111 +400,96 @@ CodeRange::CodeRange(uint32_t nameIndex, uint32_t lineNumber, FuncOffsets offset
end_ = offsets.end;
}
static inline size_t StringLength(const char *s) { return s ? strlen(s) : 0; }
static inline size_t StringLength(const char16_t *s) { return s ? js_strlen(s) : 0; }
template <class CharT>
size_t
CacheableUniquePtr<CharT>::serializedSize() const
CacheableChars::serializedSize() const
{
return sizeof(uint32_t) + StringLength(this->get()) * sizeof(CharT);
return sizeof(uint32_t) + strlen(get());
}
template <class CharT>
uint8_t*
CacheableUniquePtr<CharT>::serialize(uint8_t* cursor) const
CacheableChars::serialize(uint8_t* cursor) const
{
uint32_t length = StringLength(this->get());
uint32_t length = strlen(get());
cursor = WriteBytes(cursor, &length, sizeof(uint32_t));
cursor = WriteBytes(cursor, this->get(), length * sizeof(CharT));
cursor = WriteBytes(cursor, get(), length);
return cursor;
}
template <class CharT>
const uint8_t*
CacheableUniquePtr<CharT>::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
CacheableChars::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
{
uint32_t length;
cursor = ReadBytes(cursor, &length, sizeof(uint32_t));
this->reset(cx->pod_calloc<CharT>(length + 1));
if (!this->get())
reset(cx->pod_calloc<char>(length + 1));
if (!get())
return nullptr;
cursor = ReadBytes(cursor, this->get(), length * sizeof(CharT));
cursor = ReadBytes(cursor, get(), length);
return cursor;
}
template <class CharT>
bool
CacheableUniquePtr<CharT>::clone(JSContext* cx, CacheableUniquePtr* out) const
CacheableChars::clone(JSContext* cx, CacheableChars* out) const
{
uint32_t length = StringLength(this->get());
uint32_t length = strlen(get());
UPtr chars(cx->pod_calloc<CharT>(length + 1));
UniqueChars chars(cx->pod_calloc<char>(length + 1));
if (!chars)
return false;
PodCopy(chars.get(), this->get(), length);
PodCopy(chars.get(), get(), length);
*out = Move(chars);
return true;
}
template <class CharT>
size_t
CacheableUniquePtr<CharT>::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
CacheableChars::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return mallocSizeOf(this->get());
}
namespace js {
namespace wasm {
template struct CacheableUniquePtr<char>;
template struct CacheableUniquePtr<char16_t>;
}
return mallocSizeOf(get());
}
size_t
ExportMap::serializedSize() const
{
return SerializedVectorSize(exportNames) +
SerializedVectorSize(fieldNames) +
SerializedPodVectorSize(fieldsToExports);
return SerializedVectorSize(fieldNames) +
SerializedPodVectorSize(fieldsToExports) +
SerializedPodVectorSize(exportFuncIndices);
}
uint8_t*
ExportMap::serialize(uint8_t* cursor) const
{
cursor = SerializeVector(cursor, exportNames);
cursor = SerializeVector(cursor, fieldNames);
cursor = SerializePodVector(cursor, fieldsToExports);
cursor = SerializePodVector(cursor, exportFuncIndices);
return cursor;
}
const uint8_t*
ExportMap::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
{
(cursor = DeserializeVector(cx, cursor, &exportNames)) &&
(cursor = DeserializeVector(cx, cursor, &fieldNames)) &&
(cursor = DeserializePodVector(cx, cursor, &fieldsToExports));
(cursor = DeserializePodVector(cx, cursor, &fieldsToExports)) &&
(cursor = DeserializePodVector(cx, cursor, &exportFuncIndices));
return cursor;
}
bool
ExportMap::clone(JSContext* cx, ExportMap* map) const
{
return CloneVector(cx, exportNames, &map->exportNames) &&
CloneVector(cx, fieldNames, &map->fieldNames) &&
ClonePodVector(cx, fieldsToExports, &map->fieldsToExports);
return CloneVector(cx, fieldNames, &map->fieldNames) &&
ClonePodVector(cx, fieldsToExports, &map->fieldsToExports) &&
ClonePodVector(cx, exportFuncIndices, &map->exportFuncIndices);
}
size_t
ExportMap::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return SizeOfVectorExcludingThis(exportNames, mallocSizeOf) &&
SizeOfVectorExcludingThis(fieldNames, mallocSizeOf) &&
fieldsToExports.sizeOfExcludingThis(mallocSizeOf);
return SizeOfVectorExcludingThis(fieldNames, mallocSizeOf) &&
fieldsToExports.sizeOfExcludingThis(mallocSizeOf) &&
exportFuncIndices.sizeOfExcludingThis(mallocSizeOf);
}
size_t
@@ -529,9 +502,8 @@ ModuleData::serializedSize() const
SerializedPodVectorSize(heapAccesses) +
SerializedPodVectorSize(codeRanges) +
SerializedPodVectorSize(callSites) +
SerializedVectorSize(funcNames) +
filename.serializedSize() +
displayURL.serializedSize();
SerializedVectorSize(prettyFuncNames) +
filename.serializedSize();
}
uint8_t*
@@ -544,9 +516,8 @@ ModuleData::serialize(uint8_t* cursor) const
cursor = SerializePodVector(cursor, heapAccesses);
cursor = SerializePodVector(cursor, codeRanges);
cursor = SerializePodVector(cursor, callSites);
cursor = SerializeVector(cursor, funcNames);
cursor = SerializeVector(cursor, prettyFuncNames);
cursor = filename.serialize(cursor);
cursor = displayURL.serialize(cursor);
return cursor;
}
@@ -565,9 +536,8 @@ ModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
(cursor = DeserializePodVector(cx, cursor, &heapAccesses)) &&
(cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
(cursor = DeserializePodVector(cx, cursor, &callSites)) &&
(cursor = DeserializeVector(cx, cursor, &funcNames)) &&
(cursor = filename.deserialize(cx, cursor)) &&
(cursor = displayURL.deserialize(cx, cursor));
(cursor = DeserializeVector(cx, cursor, &prettyFuncNames)) &&
(cursor = filename.deserialize(cx, cursor));
return cursor;
}
@@ -586,9 +556,8 @@ ModuleData::clone(JSContext* cx, ModuleData* out) const
ClonePodVector(cx, heapAccesses, &out->heapAccesses) &&
ClonePodVector(cx, codeRanges, &out->codeRanges) &&
ClonePodVector(cx, callSites, &out->callSites) &&
CloneVector(cx, funcNames, &out->funcNames) &&
filename.clone(cx, &out->filename) &&
displayURL.clone(cx, &out->displayURL);
CloneVector(cx, prettyFuncNames, &out->prettyFuncNames) &&
filename.clone(cx, &out->filename);
}
size_t
@@ -600,9 +569,8 @@ ModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
heapAccesses.sizeOfExcludingThis(mallocSizeOf) +
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
callSites.sizeOfExcludingThis(mallocSizeOf) +
funcNames.sizeOfExcludingThis(mallocSizeOf) +
filename.sizeOfExcludingThis(mallocSizeOf) +
displayURL.sizeOfExcludingThis(mallocSizeOf);
prettyFuncNames.sizeOfExcludingThis(mallocSizeOf) +
filename.sizeOfExcludingThis(mallocSizeOf);
}
uint8_t*
@@ -628,7 +596,7 @@ void
Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
{
MOZ_ASSERT(usesHeap());
MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isWasm());
MOZ_ASSERT(!heap_);
MOZ_ASSERT(!rawHeapPtr());
@@ -705,41 +673,50 @@ Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap)
rawHeapPtr() = nullptr;
}
void
bool
Module::sendCodeRangesToProfiler(JSContext* cx)
{
bool enabled = false;
#ifdef JS_ION_PERF
if (PerfFuncEnabled()) {
for (const CodeRange& codeRange : module_->codeRanges) {
if (!codeRange.isFunction())
continue;
uintptr_t start = uintptr_t(code() + codeRange.begin());
uintptr_t end = uintptr_t(code() + codeRange.end());
uintptr_t size = end - start;
const char* file = module_->filename.get();
unsigned line = codeRange.funcLineNumber();
unsigned column = 0;
const char* name = module_->funcNames[codeRange.funcNameIndex()].get();
writePerfSpewerAsmJSFunctionMap(start, size, file, line, column, name);
}
}
enabled |= PerfFuncEnabled();
#endif
#ifdef MOZ_VTUNE
if (IsVTuneProfilingActive()) {
for (const CodeRange& codeRange : module_->codeRanges) {
if (!codeRange.isFunction())
continue;
enabled |= IsVTuneProfilingActive();
#endif
if (!enabled)
return true;
uintptr_t start = uintptr_t(code() + codeRange.begin());
uintptr_t end = uintptr_t(code() + codeRange.end());
uintptr_t size = end - start;
const char* name = module_->funcNames[codeRange.funcNameIndex()].get();
for (const CodeRange& codeRange : module_->codeRanges) {
if (!codeRange.isFunction())
continue;
uintptr_t start = uintptr_t(code() + codeRange.begin());
uintptr_t end = uintptr_t(code() + codeRange.end());
uintptr_t size = end - start;
UniqueChars owner;
const char* name = getFuncName(cx, codeRange.funcIndex(), &owner);
if (!name)
return false;
// Avoid "unused" warnings
(void)start;
(void)size;
(void)name;
#ifdef JS_ION_PERF
if (PerfFuncEnabled()) {
const char* file = module_->filename.get();
unsigned line = codeRange.funcLineOrBytecode();
unsigned column = 0;
writePerfSpewerAsmJSFunctionMap(start, size, file, line, column, name);
}
#endif
#ifdef MOZ_VTUNE
if (IsVTuneProfilingActive()) {
unsigned method_id = iJIT_GetNewMethodID();
if (method_id == 0)
return;
return true;
iJIT_Method_Load method;
method.method_id = method_id;
method.method_name = const_cast<char*>(name);
@@ -752,8 +729,10 @@ Module::sendCodeRangesToProfiler(JSContext* cx)
method.source_file_name = nullptr;
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&method);
}
}
#endif
}
return true;
}
bool
@@ -770,21 +749,29 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled)
// do it now since, once we start sampling, we'll be in a signal-handing
// context where we cannot malloc.
if (enabled) {
if (!funcLabels_.resize(module_->funcNames.length())) {
if (!funcLabels_.resize(module_->numFuncs)) {
ReportOutOfMemory(cx);
return false;
}
for (const CodeRange& codeRange : module_->codeRanges) {
if (!codeRange.isFunction())
continue;
unsigned lineno = codeRange.funcLineNumber();
const char* name = module_->funcNames[codeRange.funcNameIndex()].get();
UniqueChars label(JS_smprintf("%s (%s:%u)", name, module_->filename.get(), lineno));
UniqueChars owner;
const char* funcName = getFuncName(cx, codeRange.funcIndex(), &owner);
if (!funcName)
return false;
UniqueChars label(JS_smprintf("%s (%s:%u)",
funcName,
module_->filename.get(),
codeRange.funcLineOrBytecode()));
if (!label) {
ReportOutOfMemory(cx);
return false;
}
funcLabels_[codeRange.funcNameIndex()] = Move(label);
funcLabels_[codeRange.funcIndex()] = Move(label);
}
} else {
funcLabels_.clear();
@@ -836,7 +823,6 @@ Module::clone(JSContext* cx, const StaticLinkData& link, Module* out) const
// constructed. This function should clone the rest.
MOZ_ASSERT(out->module_);
out->isAsmJS_ = isAsmJS_;
out->profilingEnabled_ = profilingEnabled_;
if (!CloneVector(cx, funcLabels_, &out->funcLabels_))
@@ -865,9 +851,8 @@ Module::clone(JSContext* cx, const StaticLinkData& link, Module* out) const
}
Module::Module(UniqueModuleData module, AsmJSBool isAsmJS)
Module::Module(UniqueModuleData module)
: module_(Move(module)),
isAsmJS_(bool(isAsmJS)),
staticallyLinked_(false),
interrupt_(nullptr),
outOfBounds_(nullptr),
@@ -887,7 +872,7 @@ Module::~Module()
}
}
void
/* virtual */ void
Module::trace(JSTracer* trc)
{
for (const Import& import : imports()) {
@@ -899,6 +884,34 @@ Module::trace(JSTracer* trc)
TraceEdge(trc, &heap_, "wasm buffer");
}
/* virtual */ void
Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
{
*code += codeBytes();
*data += mallocSizeOf(this) +
globalBytes() +
mallocSizeOf(module_.get()) +
module_->sizeOfExcludingThis(mallocSizeOf) +
funcPtrTables_.sizeOfExcludingThis(mallocSizeOf) +
SizeOfVectorExcludingThis(funcLabels_, mallocSizeOf);
}
/* virtual */ bool
Module::mutedErrors() const
{
// WebAssembly code is always CORS-same-origin and so errors are never
// muted. For asm.js, muting depends on the ScriptSource containing the
// asm.js so this function is overridden by AsmJSModule.
return false;
}
/* virtual */ const char16_t*
Module::displayURL() const
{
// WebAssembly code does not have `//# sourceURL`.
return nullptr;
}
bool
Module::containsFunctionPC(void* pc) const
{
@@ -1046,10 +1059,114 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
return true;
}
bool
Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> importArgs)
static bool
WasmCall(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction callee(cx, &args.callee().as<JSFunction>());
Module& module = ExportedFunctionToModuleObject(callee)->module();
uint32_t exportIndex = ExportedFunctionToIndex(callee);
return module.callExport(cx, exportIndex, args);
}
static JSFunction*
NewExportedFunction(JSContext* cx, Handle<WasmModuleObject*> moduleObj, const ExportMap& exportMap,
uint32_t exportIndex)
{
Module& module = moduleObj->module();
unsigned numArgs = module.exports()[exportIndex].sig().args().length();
RootedAtom name(cx, module.getFuncAtom(cx, exportMap.exportFuncIndices[exportIndex]));
if (!name)
return nullptr;
JSFunction* fun = NewNativeConstructor(cx, WasmCall, numArgs, name,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
JSFunction::ASMJS_CTOR);
if (!fun)
return nullptr;
fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
return fun;
}
static bool
CreateExportObject(JSContext* cx,
Handle<WasmModuleObject*> moduleObj,
Handle<ArrayBufferObjectMaybeShared*> heap,
const ExportMap& exportMap,
const ExportVector& exports,
MutableHandleObject exportObj)
{
MOZ_ASSERT(exportMap.exportFuncIndices.length() == exports.length());
MOZ_ASSERT(exportMap.fieldNames.length() == exportMap.fieldsToExports.length());
for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) {
const char* fieldName = exportMap.fieldNames[fieldIndex].get();
if (!*fieldName) {
MOZ_ASSERT(!exportObj);
uint32_t exportIndex = exportMap.fieldsToExports[fieldIndex];
if (exportIndex == ExportMap::MemoryExport) {
MOZ_ASSERT(heap);
exportObj.set(heap);
} else {
exportObj.set(NewExportedFunction(cx, moduleObj, exportMap, exportIndex));
if (!exportObj)
return false;
}
break;
}
}
Rooted<ValueVector> vals(cx, ValueVector(cx));
for (size_t exportIndex = 0; exportIndex < exports.length(); exportIndex++) {
JSFunction* fun = NewExportedFunction(cx, moduleObj, exportMap, exportIndex);
if (!fun || !vals.append(ObjectValue(*fun)))
return false;
}
if (!exportObj) {
exportObj.set(JS_NewPlainObject(cx));
if (!exportObj)
return false;
}
for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) {
const char* fieldName = exportMap.fieldNames[fieldIndex].get();
if (!*fieldName)
continue;
JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName));
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
RootedValue val(cx);
uint32_t exportIndex = exportMap.fieldsToExports[fieldIndex];
if (exportIndex == ExportMap::MemoryExport)
val = ObjectValue(*heap);
else
val = vals[exportIndex];
if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
return false;
}
return true;
}
bool
Module::dynamicallyLink(JSContext* cx,
Handle<WasmModuleObject*> moduleObj,
Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> importArgs,
const ExportMap& exportMap,
MutableHandleObject exportObj)
{
MOZ_ASSERT(this == &moduleObj->module());
MOZ_ASSERT(staticallyLinked_);
MOZ_ASSERT(!dynamicallyLinked_);
dynamicallyLinked_ = true;
@@ -1081,94 +1198,10 @@ Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> hea
return false;
}
sendCodeRangesToProfiler(cx);
return true;
}
if (!sendCodeRangesToProfiler(cx))
return false;
static bool
WasmCall(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction callee(cx, &args.callee().as<JSFunction>());
Module& module = ExportedFunctionToModuleObject(callee)->module();
uint32_t exportIndex = ExportedFunctionToIndex(callee);
return module.callExport(cx, exportIndex, args);
}
static JSFunction*
NewExportedFunction(JSContext* cx, Handle<WasmModuleObject*> moduleObj, const ExportMap& map,
uint32_t exportIndex)
{
unsigned numArgs = moduleObj->module().exports()[exportIndex].sig().args().length();
const char* chars = map.exportNames[exportIndex].get();
RootedAtom name(cx, AtomizeUTF8Chars(cx, chars, strlen(chars)));
if (!name)
return nullptr;
JSFunction* fun = NewNativeConstructor(cx, WasmCall, numArgs, name,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
JSFunction::ASMJS_CTOR);
if (!fun)
return nullptr;
fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
return fun;
}
bool
Module::createExportObject(JSContext* cx, Handle<WasmModuleObject*> moduleObj,
const ExportMap& map, MutableHandleObject exportObj)
{
MOZ_ASSERT(this == &moduleObj->module());
MOZ_ASSERT(map.exportNames.length() == exports().length());
MOZ_ASSERT(map.fieldNames.length() == map.fieldsToExports.length());
for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) {
const char* fieldName = map.fieldNames[fieldIndex].get();
if (!*fieldName) {
MOZ_ASSERT_IF(isAsmJS(), exports().length() == 1);
MOZ_ASSERT(!exportObj);
uint32_t exportIndex = map.fieldsToExports[fieldIndex];
exportObj.set(NewExportedFunction(cx, moduleObj, map, exportIndex));
if (!exportObj)
return false;
break;
}
}
Rooted<ValueVector> vals(cx, ValueVector(cx));
for (size_t exportIndex = 0; exportIndex < exports().length(); exportIndex++) {
JSFunction* fun = NewExportedFunction(cx, moduleObj, map, exportIndex);
if (!fun || !vals.append(ObjectValue(*fun)))
return false;
}
if (!exportObj) {
exportObj.set(JS_NewPlainObject(cx));
if (!exportObj)
return false;
}
for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) {
const char* fieldName = map.fieldNames[fieldIndex].get();
if (!*fieldName)
continue;
JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName));
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
HandleValue val = vals[map.fieldsToExports[fieldIndex]];
if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
return false;
}
return true;
return CreateExportObject(cx, moduleObj, heap, exportMap, exports(), exportObj);
}
SharedMem<uint8_t*>
@@ -1268,6 +1301,8 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
break;
}
case ValType::Limit:
MOZ_CRASH("Limit");
}
}
@@ -1330,6 +1365,8 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
return false;
args.rval().set(ObjectValue(*simdObj));
break;
case ExprType::Limit:
MOZ_CRASH("Limit");
}
return true;
@@ -1393,6 +1430,7 @@ Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Val
case ValType::I32x4: MOZ_CRASH("NYI");
case ValType::F32x4: MOZ_CRASH("NYI");
case ValType::B32x4: MOZ_CRASH("NYI");
case ValType::Limit: MOZ_CRASH("Limit");
}
if (!TypeScript::ArgTypes(script, i)->hasType(type))
return true;
@@ -1407,6 +1445,43 @@ Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Val
return true;
}
const char*
Module::prettyFuncName(uint32_t funcIndex) const
{
return module_->prettyFuncNames[funcIndex].get();
}
const char*
Module::getFuncName(JSContext* cx, uint32_t funcIndex, UniqueChars* owner) const
{
if (!module_->prettyFuncNames.empty())
return prettyFuncName(funcIndex);
char* chars = JS_smprintf("wasm-function[%u]", funcIndex);
if (!chars) {
ReportOutOfMemory(cx);
return nullptr;
}
owner->reset(chars);
return chars;
}
JSAtom*
Module::getFuncAtom(JSContext* cx, uint32_t funcIndex) const
{
UniqueChars owner;
const char* chars = getFuncName(cx, funcIndex, &owner);
if (!chars)
return nullptr;
JSAtom* atom = AtomizeUTF8Chars(cx, chars, strlen(chars));
if (!atom)
return nullptr;
return atom;
}
const char*
Module::profilingLabel(uint32_t funcIndex) const
{
@@ -1415,18 +1490,6 @@ Module::profilingLabel(uint32_t funcIndex) const
return funcLabels_[funcIndex].get();
}
void
Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
{
*code += codeBytes();
*data += mallocSizeOf(this) +
globalBytes() +
mallocSizeOf(module_.get()) +
module_->sizeOfExcludingThis(mallocSizeOf) +
funcPtrTables_.sizeOfExcludingThis(mallocSizeOf) +
SizeOfVectorExcludingThis(funcLabels_, mallocSizeOf);
}
const Class WasmModuleObject::class_ = {
"WasmModuleObject",
JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
+59 -61
View File
@@ -103,14 +103,14 @@ typedef UniquePtr<StaticLinkData> UniqueStaticLinkData;
class Export
{
MallocSig sig_;
Sig sig_;
struct CacheablePod {
uint32_t stubOffset_;
} pod;
public:
Export() = default;
explicit Export(MallocSig&& sig)
explicit Export(Sig&& sig)
: sig_(Move(sig))
{
pod.stubOffset_ = UINT32_MAX;
@@ -128,7 +128,7 @@ class Export
uint32_t stubOffset() const {
return pod.stubOffset_;
}
const MallocSig& sig() const {
const Sig& sig() const {
return sig_;
}
@@ -143,7 +143,7 @@ typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
class Import
{
MallocSig sig_;
Sig sig_;
struct CacheablePod {
uint32_t exitGlobalDataOffset_;
uint32_t interpExitCodeOffset_;
@@ -153,7 +153,7 @@ class Import
public:
Import() {}
Import(Import&& rhs) : sig_(Move(rhs.sig_)), pod(rhs.pod) {}
Import(MallocSig&& sig, uint32_t exitGlobalDataOffset)
Import(Sig&& sig, uint32_t exitGlobalDataOffset)
: sig_(Move(sig))
{
pod.exitGlobalDataOffset_ = exitGlobalDataOffset;
@@ -170,18 +170,16 @@ class Import
pod.jitExitCodeOffset_ = off;
}
const MallocSig& sig() const {
const Sig& sig() const {
return sig_;
}
uint32_t exitGlobalDataOffset() const {
return pod.exitGlobalDataOffset_;
}
uint32_t interpExitCodeOffset() const {
MOZ_ASSERT(pod.interpExitCodeOffset_);
return pod.interpExitCodeOffset_;
}
uint32_t jitExitCodeOffset() const {
MOZ_ASSERT(pod.jitExitCodeOffset_);
return pod.jitExitCodeOffset_;
}
@@ -197,8 +195,8 @@ typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
class CodeRange
{
// All fields are treated as cacheable POD:
uint32_t nameIndex_;
uint32_t lineNumber_;
uint32_t funcIndex_;
uint32_t funcLineOrBytecode_;
uint32_t begin_;
uint32_t profilingReturn_;
uint32_t end_;
@@ -215,12 +213,12 @@ class CodeRange
void assertValid();
public:
enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Interrupt, Inline };
enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline };
CodeRange() = default;
CodeRange(Kind kind, Offsets offsets);
CodeRange(Kind kind, ProfilingOffsets offsets);
CodeRange(uint32_t nameIndex, uint32_t lineNumber, FuncOffsets offsets);
CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
// All CodeRanges have a begin and end.
@@ -265,13 +263,13 @@ class CodeRange
MOZ_ASSERT(isFunction());
return profilingReturn_ - u.func.profilingEpilogueToProfilingReturn_;
}
uint32_t funcNameIndex() const {
uint32_t funcIndex() const {
MOZ_ASSERT(isFunction());
return nameIndex_;
return funcIndex_;
}
uint32_t funcLineNumber() const {
uint32_t funcLineOrBytecode() const {
MOZ_ASSERT(isFunction());
return lineNumber_;
return funcLineOrBytecode_;
}
// A sorted array of CodeRanges can be looked up via BinarySearch and PC.
@@ -290,38 +288,43 @@ class CodeRange
typedef Vector<CodeRange, 0, SystemAllocPolicy> CodeRangeVector;
// A CacheableUniquePtr is used to cacheably store strings in Module.
// CacheableChars is used to cacheably store UniqueChars.
template <class CharT>
struct CacheableUniquePtr : public UniquePtr<CharT[], JS::FreePolicy>
struct CacheableChars : UniqueChars
{
typedef UniquePtr<CharT[], JS::FreePolicy> UPtr;
explicit CacheableUniquePtr(CharT* ptr) : UPtr(ptr) {}
MOZ_IMPLICIT CacheableUniquePtr(UPtr&& rhs) : UPtr(Move(rhs)) {}
CacheableUniquePtr() = default;
CacheableUniquePtr(CacheableUniquePtr&& rhs) : UPtr(Move(rhs)) {}
void operator=(CacheableUniquePtr&& rhs) { UPtr& base = *this; base = Move(rhs); }
WASM_DECLARE_SERIALIZABLE(CacheableUniquePtr)
CacheableChars() = default;
explicit CacheableChars(char* ptr) : UniqueChars(ptr) {}
MOZ_IMPLICIT CacheableChars(UniqueChars&& rhs) : UniqueChars(Move(rhs)) {}
CacheableChars(CacheableChars&& rhs) : UniqueChars(Move(rhs)) {}
void operator=(CacheableChars&& rhs) { UniqueChars::operator=(Move(rhs)); }
WASM_DECLARE_SERIALIZABLE(CacheableChars)
};
typedef CacheableUniquePtr<char> CacheableChars;
typedef CacheableUniquePtr<char16_t> CacheableTwoByteChars;
typedef Vector<CacheableChars, 0, SystemAllocPolicy> CacheableCharsVector;
// The ExportMap describes how Exports are mapped to the fields of the export
// object. This allows a single Export to be used in multiple fields.
// The 'fieldNames' vector provides the list of names of the module's exports.
// For each field in fieldNames, 'fieldsToExports' provides either:
// - the sentinel value MemoryExport indicating an export of linear memory; or
// - the index of an export (both into the module's ExportVector and the
// ExportMap's exportFuncIndices vector).
// Lastly, the 'exportFuncIndices' vector provides, for each exported function,
// the internal index of the function.
struct ExportMap
{
typedef Vector<uint32_t, 0, SystemAllocPolicy> FieldToExportVector;
static const uint32_t MemoryExport = UINT32_MAX;
CacheableCharsVector exportNames;
CacheableCharsVector fieldNames;
FieldToExportVector fieldsToExports;
Uint32Vector fieldsToExports;
Uint32Vector exportFuncIndices;
WASM_DECLARE_SERIALIZABLE(ExportMap)
};
typedef UniquePtr<ExportMap> UniqueExportMap;
// A UniqueCodePtr owns allocated executable code. Code passed to the Module
// constructor must be allocated via AllocateCode.
@@ -354,14 +357,6 @@ UsesHeap(HeapUsage heapUsage)
return bool(heapUsage);
}
// See mutedErrors comment in jsapi.h.
enum class MutedErrorsBool
{
DontMuteErrors = false,
MuteErrors = true
};
// ModuleCacheablePod holds the trivially-memcpy()able serializable portion of
// ModuleData.
@@ -370,8 +365,9 @@ struct ModuleCacheablePod
uint32_t functionBytes;
uint32_t codeBytes;
uint32_t globalBytes;
uint32_t numFuncs;
ModuleKind kind;
HeapUsage heapUsage;
MutedErrorsBool mutedErrors;
CompileArgs compileArgs;
uint32_t totalBytes() const { return codeBytes + globalBytes; }
@@ -393,9 +389,8 @@ struct ModuleData : ModuleCacheablePod
HeapAccessVector heapAccesses;
CodeRangeVector codeRanges;
CallSiteVector callSites;
CacheableCharsVector funcNames;
CacheableCharsVector prettyFuncNames;
CacheableChars filename;
CacheableTwoByteChars displayURL;
bool loadedFromCache;
WASM_DECLARE_SERIALIZABLE(ModuleData);
@@ -451,7 +446,6 @@ class Module
// Initialized when constructed:
const UniqueConstModuleData module_;
bool isAsmJS_;
// Initialized during staticallyLink:
bool staticallyLinked_;
@@ -472,14 +466,13 @@ class Module
WasmActivation*& activation();
void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
void sendCodeRangesToProfiler(JSContext* cx);
bool sendCodeRangesToProfiler(JSContext* cx);
MOZ_WARN_UNUSED_RESULT bool setProfilingEnabled(JSContext* cx, bool enabled);
ImportExit& importToExit(const Import& import);
friend class js::WasmActivation;
protected:
enum AsmJSBool { NotAsmJS = false, IsAsmJS = true };
const ModuleData& base() const { return *module_; }
bool clone(JSContext* cx, const StaticLinkData& link, Module* clone) const;
@@ -488,7 +481,7 @@ class Module
static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
static const unsigned SizeOfEntryArg = sizeof(EntryArg);
explicit Module(UniqueModuleData module, AsmJSBool = NotAsmJS);
explicit Module(UniqueModuleData module);
virtual ~Module();
virtual void trace(JSTracer* trc);
virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
@@ -500,13 +493,10 @@ class Module
HeapUsage heapUsage() const { return module_->heapUsage; }
bool usesHeap() const { return UsesHeap(module_->heapUsage); }
bool hasSharedHeap() const { return module_->heapUsage == HeapUsage::Shared; }
bool mutedErrors() const { return bool(module_->mutedErrors); }
CompileArgs compileArgs() const { return module_->compileArgs; }
const ImportVector& imports() const { return module_->imports; }
const ExportVector& exports() const { return module_->exports; }
const char* functionName(uint32_t i) const { return module_->funcNames[i].get(); }
const char* filename() const { return module_->filename.get(); }
const char16_t* displayURL() const { return module_->displayURL.get(); }
bool loadedFromCache() const { return module_->loadedFromCache; }
bool staticallyLinked() const { return staticallyLinked_; }
bool dynamicallyLinked() const { return dynamicallyLinked_; }
@@ -516,9 +506,11 @@ class Module
// semantics. The asAsmJS() member may be used as a checked downcast when
// isAsmJS() is true.
bool isAsmJS() const { return isAsmJS_; }
AsmJSModule& asAsmJS() { MOZ_ASSERT(isAsmJS_); return *(AsmJSModule*)this; }
const AsmJSModule& asAsmJS() const { MOZ_ASSERT(isAsmJS_); return *(const AsmJSModule*)this; }
bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; }
AsmJSModule& asAsmJS() { MOZ_ASSERT(isAsmJS()); return *(AsmJSModule*)this; }
const AsmJSModule& asAsmJS() const { MOZ_ASSERT(isAsmJS()); return *(const AsmJSModule*)this; }
virtual bool mutedErrors() const;
virtual const char16_t* displayURL() const;
// The range [0, functionBytes) is a subrange of [0, codeBytes) that
// contains only function body code, not the stub code. This distinction is
@@ -541,16 +533,14 @@ class Module
// This function transitions the module from a statically-linked state to a
// dynamically-linked state. If this module usesHeap(), a non-null heap
// buffer must be given. The given import vector must match the module's
// ImportVector.
// ImportVector. The function returns a new export object for this module.
bool dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> imports);
// This function creates and returns a new export object for this module.
// The lengths of exports() and map.exportNames must be the same.
bool createExportObject(JSContext* cx, Handle<WasmModuleObject*> moduleObj,
const ExportMap& map, MutableHandleObject exportObj);
bool dynamicallyLink(JSContext* cx,
Handle<WasmModuleObject*> moduleObj,
Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> imports,
const ExportMap& exportMap,
MutableHandleObject exportObj);
// The wasm heap, established by dynamicallyLink.
@@ -580,6 +570,14 @@ class Module
uint8_t* interrupt() const { MOZ_ASSERT(staticallyLinked_); return interrupt_; }
uint8_t* outOfBounds() const { MOZ_ASSERT(staticallyLinked_); return outOfBounds_; }
// Every function has an associated display atom which is either the pretty
// name given by the asm.js function name or wasm symbols or something
// generated from the function index.
const char* prettyFuncName(uint32_t funcIndex) const;
const char* getFuncName(JSContext* cx, uint32_t funcIndex, UniqueChars* owner) const;
JSAtom* getFuncAtom(JSContext* cx, uint32_t funcIndex) const;
// Each Module has a profilingEnabled state which is updated to match
// SPSProfiler::enabled() on the next Module::callExport when there are no
// frames from the Module on the stack. The ProfilingFrameIterator only
+9 -9
View File
@@ -21,7 +21,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/PodOperations.h"
#include "asmjs/AsmJS.h"
#include "asmjs/Wasm.h"
#include "asmjs/WasmModule.h"
#include "jit/AtomicOperations.h"
#include "jit/Disassembler.h"
@@ -647,7 +647,7 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
"faulting address range");
MOZ_RELEASE_ASSERT(accessAddress >= module.heap(),
"Access begins outside the asm.js heap");
MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.heap() + AsmJSMappedSize,
MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.heap() + MappedSize,
"Access extends beyond the asm.js heap guard region");
MOZ_RELEASE_ASSERT(accessAddress + access.size() > module.heap() + module.heapLength(),
"Computed access address is not actually out of bounds");
@@ -659,10 +659,10 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
// However, we wish to support the optimization of folding immediates
// and scaled indices into addresses, and any address arithmetic we fold
// gets done at full pointer width, so it doesn't get properly wrapped.
// We support this by extending AsmJSMappedSize to the greatest size
// that could be reached by such an unwrapped address, and then when we
// arrive here in the signal handler for such an access, we compute the
// fully wrapped address, and perform the load or store on it.
// We support this by extending MappedSize to the greatest size that could
// be reached by such an unwrapped address, and then when we arrive here in
// the signal handler for such an access, we compute the fully wrapped
// address, and perform the load or store on it.
//
// Taking a signal is really slow, but in theory programs really shouldn't
// be hitting this anyway.
@@ -764,7 +764,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
if (!module.usesHeap() ||
faultingAddress < module.heap() ||
faultingAddress >= module.heap() + AsmJSMappedSize)
faultingAddress >= module.heap() + MappedSize)
{
return false;
}
@@ -909,7 +909,7 @@ HandleMachException(JSRuntime* rt, const ExceptionRequest& request)
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
if (!module.usesHeap() ||
faultingAddress < module.heap() ||
faultingAddress >= module.heap() + AsmJSMappedSize)
faultingAddress >= module.heap() + MappedSize)
{
return false;
}
@@ -1119,7 +1119,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
if (!module.usesHeap() ||
faultingAddress < module.heap() ||
faultingAddress >= module.heap() + AsmJSMappedSize)
faultingAddress >= module.heap() + MappedSize)
{
return false;
}
+89 -104
View File
@@ -30,10 +30,6 @@ using namespace js::wasm;
using mozilla::ArrayLength;
using mozilla::MakeEnumeratedRange;
typedef Vector<MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef ABIArgIter<MallocSig::ArgVector> ABIArgValTypeIter;
static void
AssertStackAlignment(MacroAssembler& masm, uint32_t alignment, uint32_t addBeforeAssert = 0)
{
@@ -99,10 +95,10 @@ static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void
// function has an ABI derived from its specific signature, so this function
// must map from the ABI of CodePtr to the export's signature's ABI.
static bool
GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
GenerateEntry(ModuleGenerator& mg, unsigned exportIndex)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.exportSig(exportIndex);
const Sig& sig = mg.exportSig(exportIndex);
masm.haltingAlign(CodeAlignment);
@@ -135,7 +131,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
// ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
// effective addresses). Loading the heap register depends on the global
// register already having been loaded.
if (usesHeap)
if (mg.usesHeap())
masm.loadAsmJSHeapRegisterFromGlobalData();
// Put the 'argv' argument into a non-argument/return register so that we
@@ -240,7 +236,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
// Call into the real function.
masm.assertStackAlignment(AsmJSStackAlignment);
Label target;
target.bind(mg.funcEntryOffsets()[mg.exportFuncIndex(exportIndex)]);
target.bind(mg.exportEntryOffset(exportIndex));
masm.call(CallSiteDesc(CallSiteDesc::Relative), &target);
// Recover the stack pointer value before dynamic alignment.
@@ -276,6 +272,8 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
// We don't have control on argv alignment, do an unaligned access.
masm.storeUnalignedFloat32x4(ReturnSimd128Reg, Address(argv, 0));
break;
case ExprType::Limit:
MOZ_CRASH("Limit");
}
// Restore clobbered non-volatile registers of the caller.
@@ -293,7 +291,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
}
static void
FillArgumentArray(MacroAssembler& masm, const MallocSig::ArgVector& args, unsigned argOffset,
FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argOffset,
unsigned offsetToCallerStackArgs, Register scratch)
{
for (ABIArgValTypeIter i(args); !i.done(); i++) {
@@ -336,11 +334,10 @@ FillArgumentArray(MacroAssembler& masm, const MallocSig::ArgVector& args, unsign
// signature of the import and calls into an appropriate InvokeImport C++
// function, having boxed all the ABI arguments into a homogeneous Value array.
static bool
GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLabel,
ProfilingOffsets* offsets)
GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.importSig(importIndex);
const Sig& sig = *mg.import(importIndex).sig;
masm.setFramePushed(0);
@@ -400,11 +397,11 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLa
switch (sig.ret()) {
case ExprType::Void:
masm.call(SymbolicAddress::InvokeImport_Void);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
break;
case ExprType::I32:
masm.call(SymbolicAddress::InvokeImport_I32);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
masm.unboxInt32(argv, ReturnReg);
break;
case ExprType::I64:
@@ -413,13 +410,15 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLa
MOZ_CRASH("Float32 shouldn't be returned from a FFI");
case ExprType::F64:
masm.call(SymbolicAddress::InvokeImport_F64);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
masm.loadDouble(argv, ReturnDoubleReg);
break;
case ExprType::I32x4:
case ExprType::F32x4:
case ExprType::B32x4:
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
case ExprType::Limit:
MOZ_CRASH("Limit");
}
GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets);
@@ -441,11 +440,10 @@ static const unsigned MaybeSavedGlobalReg = 0;
// signature of the import and calls into a compatible JIT function,
// having boxed all the ABI arguments into the JIT stack frame layout.
static bool
GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
Label* throwLabel, ProfilingOffsets* offsets)
GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.importSig(importIndex);
const Sig& sig = *mg.import(importIndex).sig;
masm.setFramePushed(0);
@@ -475,7 +473,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
Register scratch = ABIArgGenerator::NonArgReturnReg1; // repeatedly clobbered
// 2.1. Get ExitDatum
unsigned globalDataOffset = mg.importExitGlobalDataOffset(importIndex);
unsigned globalDataOffset = mg.import(importIndex).globalDataOffset;
#if defined(JS_CODEGEN_X64)
masm.append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
#elif defined(JS_CODEGEN_X86)
@@ -646,7 +644,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
unsigned nativeFramePushed = masm.framePushed();
AssertStackAlignment(masm, ABIStackAlignment);
masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
masm.branchTestMagic(Assembler::Equal, JSReturnOperand, JumpTarget::Throw);
Label oolConvert;
switch (sig.ret()) {
@@ -667,6 +665,8 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
case ExprType::F32x4:
case ExprType::B32x4:
MOZ_CRASH("SIMD types shouldn't be returned from an import");
case ExprType::Limit:
MOZ_CRASH("Limit");
}
Label done;
@@ -674,7 +674,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
// Ion code does not respect system callee-saved register conventions so
// reload the heap register.
if (usesHeap)
if (mg.usesHeap())
masm.loadAsmJSHeapRegisterFromGlobalData();
GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets);
@@ -711,12 +711,12 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
switch (sig.ret()) {
case ExprType::I32:
masm.call(SymbolicAddress::CoerceInPlace_ToInt32);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
masm.unboxInt32(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnReg);
break;
case ExprType::F64:
masm.call(SymbolicAddress::CoerceInPlace_ToNumber);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
break;
default:
@@ -736,18 +736,32 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
return true;
}
static void
BindJumps(MacroAssembler& masm, JumpTarget target)
{
for (uint32_t offset : masm.jumpSites()[target]) {
RepatchLabel label;
label.use(offset);
masm.bind(&label);
}
}
// Generate a stub that is called immediately after the prologue when there is a
// stack overflow. This stub calls a C++ function to report the error and then
// jumps to the throw stub to pop the activation.
static bool
GenerateStackOverflowStub(ModuleGenerator& mg, Label* throwLabel)
GenerateStackOverflowStub(ModuleGenerator& mg)
{
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment);
if (masm.jumpSites()[JumpTarget::StackOverflow].empty())
return true;
BindJumps(masm, JumpTarget::StackOverflow);
Offsets offsets;
offsets.begin = masm.currentOffset();
masm.bind(masm.asmStackOverflowLabel());
// If we reach here via the non-profiling prologue, WasmActivation::fp has
// not been updated. To enable stack unwinding from C++, store to it now. If
@@ -766,7 +780,7 @@ GenerateStackOverflowStub(ModuleGenerator& mg, Label* throwLabel)
// No need to restore the stack; the throw stub pops everything.
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::ReportOverRecursed);
masm.jump(throwLabel);
masm.jump(JumpTarget::Throw);
if (masm.oom())
return false;
@@ -775,46 +789,22 @@ GenerateStackOverflowStub(ModuleGenerator& mg, Label* throwLabel)
return mg.defineInlineStub(offsets);
}
// Generate a stub that is called from the synchronous, inline interrupt checks
// when the interrupt flag is set. This stub calls the C++ function to handle
// the interrupt which returns whether execution has been interrupted.
static bool
GenerateSyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
{
MacroAssembler& masm = mg.masm();
masm.setFramePushed(0);
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace);
ProfilingOffsets offsets;
GenerateExitPrologue(masm, framePushed, ExitReason::Native, &offsets,
masm.asmSyncInterruptLabel());
AssertStackAlignment(masm, ABIStackAlignment);
masm.call(SymbolicAddress::HandleExecutionInterrupt);
masm.branchIfFalseBool(ReturnReg, throwLabel);
GenerateExitEpilogue(masm, framePushed, ExitReason::Native, &offsets);
if (masm.oom())
return false;
offsets.end = masm.currentOffset();
return mg.defineSyncInterruptStub(offsets);
}
// Generate a stub that is jumped to from an out-of-bounds heap access when
// there are throwing semantics. This stub calls a C++ function to report an
// error and then jumps to the throw stub to pop the activation.
static bool
GenerateConversionErrorStub(ModuleGenerator& mg, Label* throwLabel)
GenerateConversionErrorStub(ModuleGenerator& mg)
{
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment);
if (masm.jumpSites()[JumpTarget::ConversionError].empty())
return true;
BindJumps(masm, JumpTarget::ConversionError);
Offsets offsets;
offsets.begin = masm.currentOffset();
masm.bind(masm.asmOnConversionErrorLabel());
// sp can be anything at this point, so ensure it is aligned when calling
// into C++. We unconditionally jump to throw so don't worry about restoring sp.
@@ -823,7 +813,7 @@ GenerateConversionErrorStub(ModuleGenerator& mg, Label* throwLabel)
// OnImpreciseConversion always throws.
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::OnImpreciseConversion);
masm.jump(throwLabel);
masm.jump(JumpTarget::Throw);
if (masm.oom())
return false;
@@ -836,14 +826,19 @@ GenerateConversionErrorStub(ModuleGenerator& mg, Label* throwLabel)
// there are throwing semantics. This stub calls a C++ function to report an
// error and then jumps to the throw stub to pop the activation.
static bool
GenerateOutOfBoundsStub(ModuleGenerator& mg, Label* throwLabel)
GenerateOutOfBoundsStub(ModuleGenerator& mg)
{
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment);
// Generate the out-of-bounds stub unconditionally since it may always be
// used by the signal handler.
mg.defineOutOfBoundsExit(masm.currentOffset());
BindJumps(masm, JumpTarget::OutOfBounds);
Offsets offsets;
offsets.begin = masm.currentOffset();
masm.bind(masm.asmOnOutOfBoundsLabel());
// sp can be anything at this point, so ensure it is aligned when calling
// into C++. We unconditionally jump to throw so don't worry about restoring sp.
@@ -852,13 +847,13 @@ GenerateOutOfBoundsStub(ModuleGenerator& mg, Label* throwLabel)
// OnOutOfBounds always throws.
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::OnOutOfBounds);
masm.jump(throwLabel);
masm.jump(JumpTarget::Throw);
if (masm.oom())
return false;
offsets.end = masm.currentOffset();
return mg.defineOutOfBoundsStub(offsets);
return mg.defineInlineStub(offsets);
}
static const LiveRegisterSet AllRegsExceptSP(
@@ -875,11 +870,15 @@ static const LiveRegisterSet AllRegsExceptSP(
// after restoring all registers. To hack around this, push the resumePC on the
// stack so that it can be popped directly into PC.
static bool
GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
GenerateInterruptStub(ModuleGenerator& mg)
{
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment);
// Generate the interrupt stub unconditionally since it may always be used
// by the signal handler.
mg.defineInterruptExit(masm.currentOffset());
Offsets offsets;
offsets.begin = masm.currentOffset();
@@ -909,7 +908,7 @@ GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::HandleExecutionInterrupt);
masm.branchIfFalseBool(ReturnReg, throwLabel);
masm.branchIfFalseBool(ReturnReg, JumpTarget::Throw);
// Restore the StackPointer to its position before the call.
masm.moveToStackPtr(ABIArgGenerator::NonVolatileReg);
@@ -947,7 +946,7 @@ GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
masm.addToStackPtr(Imm32(4 * sizeof(intptr_t)));
masm.branchIfFalseBool(ReturnReg, throwLabel);
masm.branchIfFalseBool(ReturnReg, JumpTarget::Throw);
// This will restore stack to the address before the call.
masm.moveToStackPtr(s0);
@@ -990,7 +989,7 @@ GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::HandleExecutionInterrupt);
masm.branchIfFalseBool(ReturnReg, throwLabel);
masm.branchIfFalseBool(ReturnReg, JumpTarget::Throw);
// Restore the machine state to before the interrupt. this will set the pc!
@@ -1030,7 +1029,7 @@ GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
return false;
offsets.end = masm.currentOffset();
return mg.defineAsyncInterruptStub(offsets);
return mg.defineInlineStub(offsets);
}
// If an exception is thrown, simply pop all frames (since asm.js does not
@@ -1039,14 +1038,18 @@ GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
// 2. PopRegsInMask to restore the caller's non-volatile registers.
// 3. Return (to CallAsmJS).
static bool
GenerateThrowStub(ModuleGenerator& mg, Label* throwLabel)
GenerateThrowStub(ModuleGenerator& mg)
{
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment);
if (masm.jumpSites()[JumpTarget::Throw].empty())
return true;
BindJumps(masm, JumpTarget::Throw);
Offsets offsets;
offsets.begin = masm.currentOffset();
masm.bind(throwLabel);
// We are about to pop all frames in this WasmActivation. Set fp to null to
// maintain the invariant that fp is either null or pointing to a valid
@@ -1072,57 +1075,39 @@ GenerateThrowStub(ModuleGenerator& mg, Label* throwLabel)
}
bool
wasm::GenerateStubs(ModuleGenerator& mg, bool usesHeap)
wasm::GenerateStubs(ModuleGenerator& mg)
{
for (unsigned i = 0; i < mg.numExports(); i++) {
if (!GenerateEntry(mg, i, usesHeap))
if (!GenerateEntry(mg, i))
return false;
}
Label onThrow;
for (size_t i = 0; i < mg.numImports(); i++) {
ProfilingOffsets interp;
if (!GenerateInterpExitStub(mg, i, &onThrow, &interp))
if (!GenerateInterpExitStub(mg, i, &interp))
return false;
ProfilingOffsets jit;
if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &jit))
if (!GenerateJitExitStub(mg, i, &jit))
return false;
if (!mg.defineImport(i, interp, jit))
return false;
}
if (mg.masm().asmStackOverflowLabel()->used()) {
if (!GenerateStackOverflowStub(mg, &onThrow))
return false;
}
if (mg.masm().asmSyncInterruptLabel()->used()) {
if (!GenerateSyncInterruptStub(mg, &onThrow))
return false;
}
if (mg.masm().asmOnConversionErrorLabel()->used()) {
if (!GenerateConversionErrorStub(mg, &onThrow))
return false;
}
// Generate unconditionally: the out-of-bounds exit may be used later even
// if signal handling isn't used for out-of-bounds at the moment.
if (!GenerateOutOfBoundsStub(mg, &onThrow))
if (!GenerateStackOverflowStub(mg))
return false;
// Generate unconditionally: the async interrupt may be taken at any time.
if (!GenerateAsyncInterruptStub(mg, &onThrow))
if (!GenerateConversionErrorStub(mg))
return false;
if (onThrow.used()) {
if (!GenerateThrowStub(mg, &onThrow))
return false;
}
if (!GenerateOutOfBoundsStub(mg))
return false;
return true;
if (!GenerateInterruptStub(mg))
return false;
// The throw stub must go last since the other stubs use it.
return GenerateThrowStub(mg);
}
+1 -1
View File
@@ -25,7 +25,7 @@ namespace js {
namespace wasm {
bool
GenerateStubs(ModuleGenerator& mg, bool usesHeap);
GenerateStubs(ModuleGenerator& mg);
} // namespace wasm
} // namespace js
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasm_text_h
#define wasm_text_h
#include "asmjs/WasmBinary.h"
#include "js/Utility.h"
namespace js {
namespace wasm {
// Translate the textual representation of a wasm module (given by a
// null-terminated char16_t array) into a Bytecode object. If there is an error
// other than out-of-memory an error message string will be stored in 'error'.
extern UniqueBytecode
TextToBinary(const char16_t* text, UniqueChars* error);
} // namespace wasm
} // namespace js
#endif // wasm_text_h
+7 -2
View File
@@ -21,6 +21,7 @@
#include "jslibmath.h"
#include "jsmath.h"
#include "asmjs/Wasm.h"
#include "asmjs/WasmModule.h"
#include "js/Conversions.h"
#include "vm/Interpreter.h"
@@ -252,8 +253,12 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
CompileArgs::CompileArgs(ExclusiveContext* cx)
:
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
useSignalHandlersForOOB(cx->canUseSignalHandlers()),
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
// Signal-handling is only used to eliminate bounds checks when the OS page
// size is an even divisor of the WebAssembly page size.
useSignalHandlersForOOB(cx->canUseSignalHandlers() &&
gc::SystemPageSize() <= PageSize &&
PageSize % gc::SystemPageSize() == 0),
#else
useSignalHandlersForOOB(false),
#endif
+86 -70
View File
@@ -20,6 +20,7 @@
#define wasm_types_h
#include "mozilla/DebugOnly.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/Move.h"
@@ -41,6 +42,8 @@ using mozilla::Move;
using mozilla::DebugOnly;
using mozilla::MallocSizeOf;
typedef Vector<uint32_t, 0, SystemAllocPolicy> Uint32Vector;
// The ValType enum represents the WebAssembly "value type", which are used to
// specify the type of locals and parameters.
@@ -52,21 +55,19 @@ enum class ValType : uint8_t
F64,
I32x4,
F32x4,
B32x4
B32x4,
Limit
};
typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
static inline bool
IsSimdType(ValType vt)
{
return vt == ValType::I32x4 || vt == ValType::F32x4 || vt == ValType::B32x4;
}
static inline bool
IsSimdBoolType(ValType vt)
{
return vt == ValType::B32x4;
}
static inline jit::MIRType
ToMIRType(ValType vt)
{
@@ -78,6 +79,7 @@ ToMIRType(ValType vt)
case ValType::I32x4: return jit::MIRType_Int32x4;
case ValType::F32x4: return jit::MIRType_Float32x4;
case ValType::B32x4: return jit::MIRType_Bool32x4;
case ValType::Limit: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
}
@@ -149,7 +151,9 @@ enum class ExprType : uint8_t
I32x4 = uint8_t(ValType::I32x4),
F32x4 = uint8_t(ValType::F32x4),
B32x4 = uint8_t(ValType::B32x4),
Void
Void,
Limit
};
static inline bool
@@ -177,6 +181,12 @@ IsSimdType(ExprType et)
return IsVoid(et) ? false : IsSimdType(ValType(et));
}
static inline bool
IsSimdBoolType(ExprType vt)
{
return vt == ExprType::B32x4;
}
static inline jit::MIRType
ToMIRType(ExprType et)
{
@@ -193,85 +203,69 @@ ToMIRType(ExprType et)
// duration of module validation+compilation). Thus, long-lived objects like
// WasmModule must use malloced allocation.
template <class AllocPolicy>
class Sig
{
public:
typedef Vector<ValType, 4, AllocPolicy> ArgVector;
private:
ArgVector args_;
ValTypeVector args_;
ExprType ret_;
protected:
explicit Sig(AllocPolicy alloc = AllocPolicy()) : args_(alloc) {}
Sig(Sig&& rhs) : args_(Move(rhs.args_)), ret_(rhs.ret_) {}
Sig(ArgVector&& args, ExprType ret) : args_(Move(args)), ret_(ret) {}
Sig(const Sig&) = delete;
Sig& operator=(const Sig&) = delete;
public:
void init(ArgVector&& args, ExprType ret) {
Sig() : args_(), ret_(ExprType::Void) {}
Sig(Sig&& rhs) : args_(Move(rhs.args_)), ret_(rhs.ret_) {}
Sig(ValTypeVector&& args, ExprType ret) : args_(Move(args)), ret_(ret) {}
bool clone(const Sig& rhs) {
ret_ = rhs.ret_;
MOZ_ASSERT(args_.empty());
args_ = Move(args);
ret_ = ret;
return args_.appendAll(rhs.args_);
}
Sig& operator=(Sig&& rhs) {
ret_ = rhs.ret_;
args_ = Move(rhs.args_);
return *this;
}
ValType arg(unsigned i) const { return args_[i]; }
const ArgVector& args() const { return args_; }
const ValTypeVector& args() const { return args_; }
const ExprType& ret() const { return ret_; }
HashNumber hash() const {
HashNumber hn = HashNumber(ret_);
for (unsigned i = 0; i < args_.length(); i++)
hn = mozilla::AddToHash(hn, HashNumber(args_[i]));
return hn;
return AddContainerToHash(args_, HashNumber(ret_));
}
template <class AllocPolicy2>
bool operator==(const Sig<AllocPolicy2>& rhs) const {
if (ret() != rhs.ret())
return false;
if (args().length() != rhs.args().length())
return false;
for (unsigned i = 0; i < args().length(); i++) {
if (arg(i) != rhs.arg(i))
return false;
}
return true;
bool operator==(const Sig& rhs) const {
return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
}
template <class AllocPolicy2>
bool operator!=(const Sig<AllocPolicy2>& rhs) const {
bool operator!=(const Sig& rhs) const {
return !(*this == rhs);
}
};
class MallocSig : public Sig<SystemAllocPolicy>
struct SigHashPolicy
{
typedef Sig<SystemAllocPolicy> BaseSig;
public:
MallocSig() = default;
MallocSig(MallocSig&& rhs) : BaseSig(Move(rhs)) {}
MallocSig(ArgVector&& args, ExprType ret) : BaseSig(Move(args), ret) {}
typedef const Sig& Lookup;
static HashNumber hash(Lookup sig) { return sig.hash(); }
static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
};
class LifoSig : public Sig<LifoAllocPolicy<Fallible>>
{
typedef Sig<LifoAllocPolicy<Fallible>> BaseSig;
LifoSig(ArgVector&& args, ExprType ret) : BaseSig(Move(args), ret) {}
// A "declared" signature is a Sig object that is created and owned by the
// ModuleGenerator. These signature objects are read-only and have the same
// lifetime as the ModuleGenerator. This type is useful since some uses of Sig
// need this extended lifetime and want to statically distinguish from the
// common stack-allocated Sig objects that get passed around.
public:
static LifoSig* new_(LifoAlloc& lifo, const MallocSig& src) {
void* mem = lifo.alloc(sizeof(LifoSig));
if (!mem)
return nullptr;
ArgVector args(lifo);
if (!args.appendAll(src.args()))
return nullptr;
return new (mem) LifoSig(Move(args), src.ret());
}
struct DeclaredSig : Sig
{
DeclaredSig() = default;
DeclaredSig(DeclaredSig&& rhs) : Sig(Move(rhs)) {}
explicit DeclaredSig(Sig&& sig) : Sig(Move(sig)) {}
void operator=(Sig&& rhs) { Sig& base = *this; base = Move(rhs); }
};
typedef Vector<DeclaredSig, 0, SystemAllocPolicy> DeclaredSigVector;
typedef Vector<const DeclaredSig*, 0, SystemAllocPolicy> DeclaredSigPtrVector;
// The (,Profiling,Func)Offsets classes are used to record the offsets of
// different key points in a CodeRange during compilation.
@@ -350,8 +344,7 @@ struct FuncOffsets : ProfilingOffsets
class CallSiteDesc
{
uint32_t line_;
uint32_t column_ : 31;
uint32_t lineOrBytecode_ : 31;
uint32_t kind_ : 1;
public:
enum Kind {
@@ -360,15 +353,14 @@ class CallSiteDesc
};
CallSiteDesc() {}
explicit CallSiteDesc(Kind kind)
: line_(0), column_(0), kind_(kind)
: lineOrBytecode_(0), kind_(kind)
{}
CallSiteDesc(uint32_t line, uint32_t column, Kind kind)
: line_(line), column_(column), kind_(kind)
CallSiteDesc(uint32_t lineOrBytecode, Kind kind)
: lineOrBytecode_(lineOrBytecode), kind_(kind)
{
MOZ_ASSERT(column_ == column, "column must fit in 31 bits");
MOZ_ASSERT(lineOrBytecode_ == lineOrBytecode, "must fit in 31 bits");
}
uint32_t line() const { return line_; }
uint32_t column() const { return column_; }
uint32_t lineOrBytecode() const { return lineOrBytecode_; }
Kind kind() const { return Kind(kind_); }
};
@@ -570,6 +562,22 @@ enum class SymbolicAddress
void*
AddressOf(SymbolicAddress imm, ExclusiveContext* cx);
// A wasm::JumpTarget represents one of a special set of stubs that can be
// jumped to from any function. Because wasm modules can be larger than the
// range of a plain jump, these potentially out-of-range jumps must be recorded
// and patched specially by the MacroAssembler and ModuleGenerator.
enum class JumpTarget
{
StackOverflow,
OutOfBounds,
ConversionError,
Throw,
Limit
};
typedef mozilla::EnumeratedArray<JumpTarget, JumpTarget::Limit, Uint32Vector> JumpSiteArray;
// The CompileArgs struct captures global parameters that affect all wasm code
// generation. It also currently is the single source of truth for whether or
// not to use signal handlers for different purposes.
@@ -585,6 +593,14 @@ struct CompileArgs
bool operator!=(CompileArgs rhs) const { return !(*this == rhs); }
};
// A Module can either be asm.js or wasm.
enum ModuleKind
{
Wasm,
AsmJS
};
// Constants:
static const unsigned ActivationGlobalDataOffset = 0;
+38 -6
View File
@@ -14,8 +14,8 @@ function ArrayIndexOf(searchElement/*, fromIndex*/) {
if (len === 0)
return -1;
/* Step 5. */
var n = arguments.length > 1 ? ToInteger(arguments[1]) : 0;
/* Step 5. Add zero to convert -0 to +0, per ES6 5.2. */
var n = arguments.length > 1 ? ToInteger(arguments[1]) + 0 : 0;
/* Step 6. */
if (n >= len)
@@ -70,8 +70,8 @@ function ArrayLastIndexOf(searchElement/*, fromIndex*/) {
if (len === 0)
return -1;
/* Step 5. */
var n = arguments.length > 1 ? ToInteger(arguments[1]) : len - 1;
/* Step 5. Add zero to convert -0 to +0, per ES6 5.2. */
var n = arguments.length > 1 ? ToInteger(arguments[1]) + 0 : len - 1;
/* Steps 6-7. */
var k;
@@ -195,6 +195,36 @@ function ArrayStaticSome(list, callbackfn/*, thisArg*/) {
return callFunction(ArraySome, list, callbackfn, T);
}
/* ES6 draft 2016-1-15 22.1.3.25 Array.prototype.sort (comparefn) */
function ArraySort(comparefn) {
/* Step 1. */
var O = ToObject(this);
/* Step 2. */
var len = TO_UINT32(O.length);
/* 22.1.3.25.1 Runtime Semantics: SortCompare( x, y ) */
var wrappedCompareFn = comparefn;
comparefn = function(x, y) {
/* Steps 1-3. */
if (x === undefined) {
if (y === undefined)
return 0;
return 1;
}
if (y === undefined)
return -1;
/* Step 4.a. */
var v = ToNumber(wrappedCompareFn(x, y));
/* Step 4.b-c. */
return v !== v ? 0 : v;
}
return MergeSort(O, len, comparefn);
}
/* ES5 15.4.4.18. */
function ArrayForEach(callbackfn/*, thisArg*/) {
/* Step 1. */
@@ -690,15 +720,17 @@ function ArrayIteratorNext() {
return callFunction(CallArrayIteratorMethodIfWrapped, this,
"ArrayIteratorNext");
}
var a = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET);
// The index might not be an integer, so we have to do a generic get here.
var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND);
var result = { value: undefined, done: false };
var len = IsPossiblyWrappedTypedArray(a)
? PossiblyWrappedTypedArrayLength(a)
: TO_UINT32(a.length);
// FIXME: This should be ToLength, which clamps at 2**53. Bug 924058.
if (index >= TO_UINT32(a.length)) {
if (index >= len) {
// When the above is changed to ToLength, use +1/0 here instead
// of MAX_UINT32.
UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 0xffffffff);
+1
View File
@@ -9,6 +9,7 @@
#include "builtin/SelfHostingDefines.h"
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
#include "gc/Policy.h"
#include "gc/Tracer.h"
#include "jsobjinlines.h"
+6 -1
View File
@@ -114,7 +114,12 @@ Reflect_construct(JSContext* cx, unsigned argc, Value* vp)
return false;
// Step 6.
return Construct(cx, args.get(0), constructArgs, newTarget, args.rval());
RootedObject obj(cx);
if (!Construct(cx, args.get(0), constructArgs, newTarget, &obj))
return false;
args.rval().setObject(*obj);
return true;
}
/* ES6 26.1.3 Reflect.defineProperty(target, propertyKey, attributes) */
+89 -18
View File
@@ -37,8 +37,38 @@ using mozilla::NumberIsInt32;
///////////////////////////////////////////////////////////////////////////
// SIMD
static_assert(unsigned(SimdType::Count) == 12, "sync with TypedObjectConstants.h");
bool
js::IsSignedIntSimdType(SimdType type)
{
switch (type) {
case SimdType::Int32x4:
return true;
case SimdType::Float32x4:
case SimdType::Bool32x4:
return false;
default:
break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown SIMD type");
}
PropertyName*
js::SimdTypeToName(JSContext* cx, SimdType type)
{
switch (type) {
case SimdType::Int32x4: return cx->names().Int32x4;
case SimdType::Float32x4: return cx->names().Float32x4;
case SimdType::Bool32x4: return cx->names().Bool32x4;
default: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
}
static bool
CheckVectorObject(HandleValue v, SimdTypeDescr::Type expectedType)
CheckVectorObject(HandleValue v, SimdType expectedType)
{
if (!v.isObject())
return false;
@@ -162,7 +192,7 @@ namespace {
#define DEFINE_DEFN_(TypeName) \
class TypeName##Defn { \
public: \
static const SimdTypeDescr::Type type = SimdTypeDescr::TypeName; \
static const SimdType type = SimdType::TypeName; \
static const JSFunctionSpec Methods[]; \
};
@@ -185,9 +215,50 @@ static const JSFunctionSpec SimdTypedObjectMethods[] = {
JS_FS_END
};
// Provide JSJitInfo structs for those types that are supported by Ion.
// The controlling SIMD type is encoded as the InlinableNative primary opcode.
// The SimdOperation within the type is encoded in the .depth field.
//
// The JS_INLINABLE_FN macro refers to js::JitInfo_##native which we provide as
// Simd##Type##_##Operation
namespace js {
namespace jit {
// See also JitInfo_* in MCallOptimize.cpp. We provide a JSJitInfo for all the
// named functions here. The default JitInfo_SimdInt32x4 etc structs represent the
// SimdOperation::Constructor.
#define DEFN(TYPE, OP) const JSJitInfo JitInfo_Simd##TYPE##_##OP = { \
/* .getter, unused for inlinable natives. */ \
{ nullptr }, \
/* .inlinableNative, but we have to init first union member: .protoID. */ \
{ uint16_t(InlinableNative::Simd##TYPE) }, \
/* .nativeOp. Actually initializing first union member .depth. */ \
{ uint16_t(SimdOperation::Fn_##OP) }, \
/* .type_ bitfield says this in an inlinable native function. */ \
JSJitInfo::InlinableNative \
/* Remaining fields are not used for inlinable natives. They are zero-initialized. */ \
};
// This list of inlinable types should match the one in jit/InlinableNatives.h.
#define TDEFN(Name, Func, Operands) DEFN(Float32x4, Name)
FLOAT32X4_FUNCTION_LIST(TDEFN)
#undef TDEFN
#define TDEFN(Name, Func, Operands) DEFN(Int32x4, Name)
INT32X4_FUNCTION_LIST(TDEFN)
#undef TDEFN
#define TDEFN(Name, Func, Operands) DEFN(Bool32x4, Name)
BOOL32X4_FUNCTION_LIST(TDEFN)
#undef TDEFN
} // namespace jit
} // namespace js
const JSFunctionSpec Float32x4Defn::Methods[] = {
#define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands) \
JS_INLINABLE_FN(#Name, js::simd_float32x4_##Name, Operands, 0, SimdFloat32x4),
JS_INLINABLE_FN(#Name, js::simd_float32x4_##Name, Operands, 0, SimdFloat32x4_##Name),
FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM)
#undef SIMD_FLOAT32x4_FUNCTION_ITEM
JS_FS_END
@@ -219,7 +290,7 @@ const JSFunctionSpec Int16x8Defn::Methods[] = {
const JSFunctionSpec Int32x4Defn::Methods[] = {
#define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands) \
JS_INLINABLE_FN(#Name, js::simd_int32x4_##Name, Operands, 0, SimdInt32x4),
JS_INLINABLE_FN(#Name, js::simd_int32x4_##Name, Operands, 0, SimdInt32x4_##Name),
INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM)
#undef SIMD_INT32X4_FUNCTION_ITEM
JS_FS_END
@@ -310,10 +381,11 @@ SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
return false;
#define CASE_CALL_(Type) \
case SimdTypeDescr::Type: return FillLanes< ::Type>(cx, result, args);
case SimdType::Type: return FillLanes< ::Type>(cx, result, args);
switch (descr->type()) {
FOR_EACH_SIMD(CASE_CALL_)
case SimdType::Count: break;
}
#undef CASE_CALL_
@@ -324,11 +396,9 @@ SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
///////////////////////////////////////////////////////////////////////////
// SIMD class
static const uint32_t SIMD_SLOTS_COUNT = SimdTypeDescr::LAST_TYPE + 1;
const Class SIMDObject::class_ = {
const Class SimdObject::class_ = {
"SIMD",
JSCLASS_HAS_RESERVED_SLOTS(SIMD_SLOTS_COUNT),
JSCLASS_HAS_RESERVED_SLOTS(uint32_t(SimdType::Count)),
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
@@ -353,7 +423,7 @@ GlobalObject::initSimdObject(JSContext* cx, Handle<GlobalObject*> global)
if (!objProto)
return false;
globalSimdObject = NewObjectWithGivenProto(cx, &SIMDObject::class_, objProto, SingletonObject);
globalSimdObject = NewObjectWithGivenProto(cx, &SimdObject::class_, objProto, SingletonObject);
if (!globalSimdObject)
return false;
@@ -382,13 +452,13 @@ CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName s
if (!typeDescr)
return false;
const SimdTypeDescr::Type type = T::type;
const SimdType simdType = T::type;
typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd));
typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type)));
typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type)));
typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(simdType)));
typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(simdType)));
typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(uint8_t(simdType)));
if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr))
return false;
@@ -435,10 +505,11 @@ bool
GlobalObject::initSimdType(JSContext* cx, Handle<GlobalObject*> global, uint32_t simdTypeDescrType)
{
#define CREATE_(Type) \
case SimdTypeDescr::Type: return CreateSimdType<Type##Defn>(cx, global, cx->names().Type);
case SimdType::Type: return CreateSimdType<Type##Defn>(cx, global, cx->names().Type);
switch (SimdTypeDescr::Type(simdTypeDescrType)) {
switch (SimdType(simdTypeDescrType)) {
FOR_EACH_SIMD(CREATE_)
case SimdType::Count: break;
}
MOZ_CRASH("unexpected simd type");
@@ -446,7 +517,7 @@ GlobalObject::initSimdType(JSContext* cx, Handle<GlobalObject*> global, uint32_t
}
bool
SIMDObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved)
SimdObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved)
{
*resolved = false;
if (!JSID_IS_ATOM(id))
@@ -464,7 +535,7 @@ SIMDObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool*
}
JSObject*
js::InitSIMDClass(JSContext* cx, HandleObject obj)
js::InitSimdClass(JSContext* cx, HandleObject obj)
{
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
return global->getOrCreateSimdGlobalObject(cx);
+71 -16
View File
@@ -779,7 +779,58 @@
namespace js {
class SIMDObject : public JSObject
// Complete set of SIMD types.
// It must be kept in sync with the enumeration of values in
// TypedObjectConstants.h; in particular we need to ensure that Count is
// appropriately set with respect to the number of actual types.
enum class SimdType : uint8_t {
Int8x16 = JS_SIMDTYPEREPR_INT8X16,
Int16x8 = JS_SIMDTYPEREPR_INT16X8,
Int32x4 = JS_SIMDTYPEREPR_INT32X4,
Uint8x16 = JS_SIMDTYPEREPR_UINT8X16,
Uint16x8 = JS_SIMDTYPEREPR_UINT16X8,
Uint32x4 = JS_SIMDTYPEREPR_UINT32X4,
Float32x4 = JS_SIMDTYPEREPR_FLOAT32X4,
Float64x2 = JS_SIMDTYPEREPR_FLOAT64X2,
Bool8x16 = JS_SIMDTYPEREPR_BOOL8X16,
Bool16x8 = JS_SIMDTYPEREPR_BOOL16X8,
Bool32x4 = JS_SIMDTYPEREPR_BOOL32X4,
Bool64x2 = JS_SIMDTYPEREPR_BOOL64X2,
Count
};
// Complete set of SIMD operations.
//
// No SIMD types implement all of these operations.
//
// C++ defines keywords and/or/xor/not, so prepend Fn_ to all named functions to
// avoid clashes.
enum class SimdOperation : uint8_t {
// The constructor call. No Fn_ prefix here.
Constructor,
// All the operations, except for casts.
#define DEFOP(x) Fn_##x,
FORALL_SIMD_NONCAST_OP(DEFOP)
#undef DEFOP
// Int <-> Float conversions.
Fn_fromInt32x4,
Fn_fromUint32x4,
Fn_fromFloat32x4,
// Bitcasts. One for each type with a memory representation.
Fn_fromInt8x16Bits,
Fn_fromInt16x8Bits,
Fn_fromInt32x4Bits,
Fn_fromUint8x16Bits,
Fn_fromUint16x8Bits,
Fn_fromUint32x4Bits,
Fn_fromFloat32x4Bits,
Fn_fromFloat64x2Bits,
};
class SimdObject : public JSObject
{
public:
static const Class class_;
@@ -792,8 +843,8 @@ class SIMDObject : public JSObject
// of the SIMD vector.
// - requires static const unsigned lanes: this is the number of lanes (length)
// of the SIMD vector.
// - requires static const SimdTypeDescr::Type type: this is the SimdTypeDescr
// enum value corresponding to the SIMD type.
// - requires static const SimdType type: this is the SimdType enum value
// corresponding to the SIMD type.
// - requires static bool Cast(JSContext*, JS::HandleValue, Elem*): casts a
// given Value to the current scalar lane type and saves it in the Elem
// out-param.
@@ -807,7 +858,7 @@ class SIMDObject : public JSObject
struct Float32x4 {
typedef float Elem;
static const unsigned lanes = 4;
static const SimdTypeDescr::Type type = SimdTypeDescr::Float32x4;
static const SimdType type = SimdType::Float32x4;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
double d;
if (!ToNumber(cx, v, &d))
@@ -823,7 +874,7 @@ struct Float32x4 {
struct Float64x2 {
typedef double Elem;
static const unsigned lanes = 2;
static const SimdTypeDescr::Type type = SimdTypeDescr::Float64x2;
static const SimdType type = SimdType::Float64x2;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
return ToNumber(cx, v, out);
}
@@ -835,7 +886,7 @@ struct Float64x2 {
struct Int8x16 {
typedef int8_t Elem;
static const unsigned lanes = 16;
static const SimdTypeDescr::Type type = SimdTypeDescr::Int8x16;
static const SimdType type = SimdType::Int8x16;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
return ToInt8(cx, v, out);
}
@@ -847,7 +898,7 @@ struct Int8x16 {
struct Int16x8 {
typedef int16_t Elem;
static const unsigned lanes = 8;
static const SimdTypeDescr::Type type = SimdTypeDescr::Int16x8;
static const SimdType type = SimdType::Int16x8;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
return ToInt16(cx, v, out);
}
@@ -859,7 +910,7 @@ struct Int16x8 {
struct Int32x4 {
typedef int32_t Elem;
static const unsigned lanes = 4;
static const SimdTypeDescr::Type type = SimdTypeDescr::Int32x4;
static const SimdType type = SimdType::Int32x4;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
return ToInt32(cx, v, out);
}
@@ -871,7 +922,7 @@ struct Int32x4 {
struct Uint8x16 {
typedef uint8_t Elem;
static const unsigned lanes = 16;
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint8x16;
static const SimdType type = SimdType::Uint8x16;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
return ToUint8(cx, v, out);
}
@@ -883,7 +934,7 @@ struct Uint8x16 {
struct Uint16x8 {
typedef uint16_t Elem;
static const unsigned lanes = 8;
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint16x8;
static const SimdType type = SimdType::Uint16x8;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
return ToUint16(cx, v, out);
}
@@ -895,7 +946,7 @@ struct Uint16x8 {
struct Uint32x4 {
typedef uint32_t Elem;
static const unsigned lanes = 4;
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint32x4;
static const SimdType type = SimdType::Uint32x4;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
return ToUint32(cx, v, out);
}
@@ -907,7 +958,7 @@ struct Uint32x4 {
struct Bool8x16 {
typedef int8_t Elem;
static const unsigned lanes = 16;
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool8x16;
static const SimdType type = SimdType::Bool8x16;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
*out = ToBoolean(v) ? -1 : 0;
return true;
@@ -920,7 +971,7 @@ struct Bool8x16 {
struct Bool16x8 {
typedef int16_t Elem;
static const unsigned lanes = 8;
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool16x8;
static const SimdType type = SimdType::Bool16x8;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
*out = ToBoolean(v) ? -1 : 0;
return true;
@@ -933,7 +984,7 @@ struct Bool16x8 {
struct Bool32x4 {
typedef int32_t Elem;
static const unsigned lanes = 4;
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool32x4;
static const SimdType type = SimdType::Bool32x4;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
*out = ToBoolean(v) ? -1 : 0;
return true;
@@ -946,7 +997,7 @@ struct Bool32x4 {
struct Bool64x2 {
typedef int64_t Elem;
static const unsigned lanes = 2;
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool64x2;
static const SimdType type = SimdType::Bool64x2;
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
*out = ToBoolean(v) ? -1 : 0;
return true;
@@ -956,6 +1007,10 @@ struct Bool64x2 {
}
};
bool IsSignedIntSimdType(SimdType type);
PropertyName* SimdTypeToName(JSContext* cx, SimdType type);
template<typename V>
JSObject* CreateSimd(JSContext* cx, const typename V::Elem* data);
@@ -1038,7 +1093,7 @@ BOOL64X2_FUNCTION_LIST(DECLARE_SIMD_BOOL64x2_FUNCTION)
#undef DECLARE_SIMD_BOOL64x2_FUNCTION
JSObject*
InitSIMDClass(JSContext* cx, HandleObject obj);
InitSimdClass(JSContext* cx, HandleObject obj);
} /* namespace js */
+185
View File
@@ -0,0 +1,185 @@
/* 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 use varying sorts across the self-hosted codebase. All sorts are
// consolidated here to avoid confusion and re-implementation of existing
// algorithms.
// For sorting small arrays.
function InsertionSort(array, from, to, comparefn) {
var item, swap;
for (var i = from + 1; i <= to; i++) {
item = array[i];
for (var j = i - 1; j >= from; j--) {
swap = array[j];
if (comparefn(swap, item) <= 0)
break;
array[j + 1] = swap;
}
array[j + 1] = item;
}
}
function SwapArrayElements(array, i, j) {
var swap = array[i];
array[i] = array[j];
array[j] = swap;
}
// A helper function for MergeSort.
function Merge(array, start, mid, end, lBuffer, rBuffer, comparefn) {
var i, j, k;
var sizeLeft = mid - start + 1;
var sizeRight = end - mid;
// Copy our virtual arrays into separate buffers.
for (i = 0; i < sizeLeft; i++)
lBuffer[i] = array[start + i];
for (j = 0; j < sizeRight; j++)
rBuffer[j] = array[mid + 1 + j];
i = 0;
j = 0;
k = start;
while (i < sizeLeft && j < sizeRight) {
if (comparefn(lBuffer[i], rBuffer[j]) <= 0) {
array[k] = lBuffer[i];
i++;
} else {
array[k] = rBuffer[j];
j++;
}
k++;
}
// Empty out any remaining elements in the buffer.
while (i < sizeLeft) {
array[k] = lBuffer[i];
i++;
k++;
}
while (j < sizeRight) {
array[k] = rBuffer[j];
j++;
k++;
}
}
// Iterative, bottom up, mergesort.
function MergeSort(array, len, comparefn) {
// Insertion sort for small arrays, where "small" is defined by performance
// testing.
if (len < 24) {
InsertionSort(array, 0, len - 1, comparefn);
return array;
}
// We do all of our allocating up front
var lBuffer = new List();
var rBuffer = new List();
var mid, end, endOne, endTwo;
for (var windowSize = 1; windowSize < len; windowSize = 2*windowSize) {
for (var start = 0; start < len - 1; start += 2*windowSize) {
assert(windowSize < len, "The window size is larger than the array length!");
// The midpoint between the two subarrays.
mid = start + windowSize - 1;
// To keep from going over the edge.
end = start + 2 * windowSize - 1;
end = end < len - 1 ? end : len - 1;
// Skip lopsided runs to avoid doing useless work
if (mid > end)
continue;
Merge(array, start, mid, end, lBuffer, rBuffer, comparefn);
}
}
return array;
}
// Rearranges the elements in array[from:to + 1] and returns an index j such that:
// - from < j < to
// - each element in array[from:j] is less than or equal to array[j]
// - each element in array[j + 1:to + 1] greater than or equal to array[j].
function Partition(array, from, to, comparefn) {
assert(to - from >= 3, "Partition will not work with less than three elements");
var medianIndex = (from + to) >> 1;
var i = from + 1;
var j = to;
SwapArrayElements(array, medianIndex, i);
// Median of three pivot selection.
if (comparefn(array[from], array[to]) > 0)
SwapArrayElements(array, from, to);
if (comparefn(array[i], array[to]) > 0)
SwapArrayElements(array, i, to);
if (comparefn(array[from], array[i]) > 0)
SwapArrayElements(array, from, i);
var pivotIndex = i;
// Hoare partition method.
for(;;) {
do i++; while (comparefn(array[i], array[pivotIndex]) < 0);
do j--; while (comparefn(array[j], array[pivotIndex]) > 0);
if (i > j)
break;
SwapArrayElements(array, i, j);
}
SwapArrayElements(array, pivotIndex, j);
return j;
}
// In-place QuickSort.
function QuickSort(array, len, comparefn) {
// Managing the stack ourselves seems to provide a small performance boost.
var stack = new List();
var top = 0;
var start = 0;
var end = len - 1;
var pivotIndex, i, j, leftLen, rightLen;
for (;;) {
// Insertion sort for the first N elements where N is some value
// determined by performance testing.
if (end - start <= 23) {
InsertionSort(array, start, end, comparefn);
if (top < 1)
break;
end = stack[--top];
start = stack[--top];
} else {
pivotIndex = Partition(array, start, end, comparefn);
// Calculate the left and right sub-array lengths and save
// stack space by directly modifying start/end so that
// we sort the longest of the two during the next iteration.
// This reduces the maximum stack size to log2(len).
leftLen = (pivotIndex - 1) - start;
rightLen = end - (pivotIndex + 1);
if (rightLen > leftLen) {
stack[top++] = start;
stack[top++] = pivotIndex - 1;
start = pivotIndex + 1;
} else {
stack[top++] = pivotIndex + 1;
stack[top++] = end;
end = pivotIndex - 1;
}
}
}
return array;
}
+44 -25
View File
@@ -19,6 +19,7 @@
#include "jswrapper.h"
#include "asmjs/AsmJS.h"
#include "asmjs/Wasm.h"
#include "jit/InlinableNatives.h"
#include "jit/JitFrameIterator.h"
#include "js/Debug.h"
@@ -615,10 +616,10 @@ ScheduleGC(JSContext* cx, unsigned argc, Value* vp)
PrepareZoneForGC(args[0].toString()->zone());
}
uint8_t zeal;
uint32_t zealBits;
uint32_t freq;
uint32_t next;
JS_GetGCZeal(cx, &zeal, &freq, &next);
JS_GetGCZealBits(cx, &zealBits, &freq, &next);
args.rval().setInt32(next);
return true;
}
@@ -725,6 +726,40 @@ DeterministicGC(JSContext* cx, unsigned argc, Value* vp)
}
#endif /* JS_GC_ZEAL */
static bool
ValidateGC(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1) {
RootedObject callee(cx, &args.callee());
ReportUsageError(cx, callee, "Wrong number of arguments");
return false;
}
#ifndef JS_GC_ZEAL
RootedObject callee(cx, &args.callee());
ReportUsageError(cx, callee, "Called ValidateGC in a build without GC Zeal.");
return false;
#else
uint8_t zeal;
uint32_t freq;
uint32_t scheduled;
cx->runtime()->gc.getZeal(&zeal, &freq, &scheduled);
if (zeal != 0 && zeal != js::gc::ZealIncrementalMarkingValidator) {
RootedObject callee(cx, &args.callee());
ReportUsageError(cx, callee, "Attempting to enter Marking Validation while another Zeal "
"mode is set.");
return false;
}
int zealMode = ToBoolean(args[0]) ? 11 : 0;
JS_SetGCZeal(cx, zealMode, 0);
args.rval().setUndefined();
return true;
#endif // JS_GC_ZEAL
}
static bool
StartGC(JSContext* cx, unsigned argc, Value* vp)
{
@@ -812,22 +847,6 @@ AbortGC(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
ValidateGC(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1) {
RootedObject callee(cx, &args.callee());
ReportUsageError(cx, callee, "Wrong number of arguments");
return false;
}
cx->runtime()->gc.setValidate(ToBoolean(args[0]));
args.rval().setUndefined();
return true;
}
static bool
FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp)
{
@@ -2046,13 +2065,14 @@ Neuter(JSContext* cx, unsigned argc, Value* vp)
return false;
}
NeuterDataDisposition changeData;
RootedString str(cx, JS::ToString(cx, args[1]));
if (!str)
return false;
JSAutoByteString dataDisposition(cx, str);
if (!dataDisposition)
return false;
DetachDataDisposition changeData;
if (strcmp(dataDisposition.ptr(), "same-data") == 0) {
changeData = KeepData;
} else if (strcmp(dataDisposition.ptr(), "change-data") == 0) {
@@ -2062,7 +2082,7 @@ Neuter(JSContext* cx, unsigned argc, Value* vp)
return false;
}
if (!JS_NeuterArrayBuffer(cx, obj, changeData))
if (!JS_DetachArrayBuffer(cx, obj, changeData))
return false;
args.rval().setUndefined();
@@ -2138,11 +2158,7 @@ static bool
SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
#ifdef ENABLE_SHARED_ARRAY_BUFFER
args.rval().setBoolean(true);
#else
args.rval().setBoolean(false);
#endif
args.rval().setBoolean(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
return true;
}
@@ -3666,6 +3682,9 @@ js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_,
disableOOMFunctions = disableOOMFunctions_;
if (!wasm::DefineTestingFunctions(cx, obj))
return false;
if (!JS_DefineProperties(cx, obj, TestingProperties))
return false;
+231 -47
View File
@@ -4,6 +4,44 @@
#include "TypedObjectConstants.h"
function ViewedArrayBufferIfReified(tarray) {
assert(IsTypedArray(tarray), "non-typed array asked for its buffer");
var buf = UnsafeGetReservedSlot(tarray, JS_TYPEDARRAYLAYOUT_BUFFER_SLOT);
assert(buf === null || (IsObject(buf) && (IsArrayBuffer(buf) || IsSharedArrayBuffer(buf))),
"unexpected value in buffer slot");
return buf;
}
function IsDetachedBuffer(buffer) {
// A typed array with a null buffer has never had its buffer exposed to
// become detached.
if (buffer === null)
return false;
assert(IsArrayBuffer(buffer) || IsSharedArrayBuffer(buffer),
"non-ArrayBuffer passed to IsDetachedBuffer");
// Shared array buffers are not detachable.
//
// This check is more expensive than desirable, but IsDetachedBuffer is
// only hot for non-shared memory in SetFromNonTypedArray, so there is an
// optimization in place there to avoid incurring the cost here. An
// alternative is to give SharedArrayBuffer the same layout as ArrayBuffer.
if (IsSharedArrayBuffer(buffer))
return false;
var flags = UnsafeGetInt32FromReservedSlot(buffer, JS_ARRAYBUFFER_FLAGS_SLOT);
return (flags & JS_ARRAYBUFFER_DETACHED_FLAG) !== 0;
}
function GetAttachedArrayBuffer(tarray) {
var buffer = ViewedArrayBufferIfReified(tarray);
if (IsDetachedBuffer(buffer))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
return buffer;
}
// ES6 draft 20150304 %TypedArray%.prototype.copyWithin
function TypedArrayCopyWithin(target, start, end = undefined) {
// This function is not generic.
@@ -12,7 +50,7 @@ function TypedArrayCopyWithin(target, start, end = undefined) {
"TypedArrayCopyWithin");
}
// Bug 1101256: detachment checks mandated by ValidateTypedArray
GetAttachedArrayBuffer(this);
// Steps 1-2.
var obj = this;
@@ -62,8 +100,9 @@ function TypedArrayCopyWithin(target, start, end = undefined) {
// Steps 15-17.
//
// Note that getting or setting a typed array element must throw if the
// typed array is neutered, so the intrinsic below checks for neutering.
// This happens *only* if a get/set occurs, i.e. when |count > 0|.
// underlying buffer is detached, so the intrinsic below checks for
// detachment. This happens *only* if a get/set occurs, i.e. when
// |count > 0|.
//
// Also note that this copies elements effectively by memmove, *not* in
// step 17's specified order. This is unobservable, but it would be if we
@@ -85,7 +124,8 @@ function TypedArrayEntries() {
return callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayEntries");
}
// Step 4-6. Bug 1101256: detachment checks
// Step 4-6.
GetAttachedArrayBuffer(O);
// Step 7.
return CreateArrayIterator(O, ITEM_KIND_KEY_AND_VALUE);
@@ -99,6 +139,8 @@ function TypedArrayEvery(callbackfn, thisArg = undefined) {
"TypedArrayEvery");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -140,6 +182,8 @@ function TypedArrayFill(value, start = 0, end = undefined) {
"TypedArrayFill");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -183,6 +227,8 @@ function TypedArrayFilter(callbackfn, thisArg = undefined) {
"TypedArrayFilter");
}
GetAttachedArrayBuffer(O);
// Step 4.
var len = TypedArrayLength(O);
@@ -243,6 +289,8 @@ function TypedArrayFind(predicate, thisArg = undefined) {
"TypedArrayFind");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -280,6 +328,8 @@ function TypedArrayFindIndex(predicate, thisArg = undefined) {
"TypedArrayFindIndex");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -315,6 +365,8 @@ function TypedArrayForEach(callbackfn, thisArg = undefined) {
"TypedArrayForEach");
}
GetAttachedArrayBuffer(this);
// Step 1-2.
var O = this;
@@ -350,6 +402,8 @@ function TypedArrayIndexOf(searchElement, fromIndex = 0) {
"TypedArrayIndexOf");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -360,8 +414,8 @@ function TypedArrayIndexOf(searchElement, fromIndex = 0) {
if (len === 0)
return -1;
// Steps 7-8.
var n = ToInteger(fromIndex);
// Steps 7-8. Add zero to convert -0 to +0, per ES6 5.2.
var n = ToInteger(fromIndex) + 0;
// Step 9.
if (n >= len)
@@ -399,6 +453,8 @@ function TypedArrayJoin(separator) {
return callFunction(CallTypedArrayMethodIfWrapped, this, separator, "TypedArrayJoin");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -439,19 +495,19 @@ function TypedArrayJoin(separator) {
return R;
}
// ES6 draft rev30 (2014/12/24) 22.2.3.15 %TypedArray%.prototype.keys()
// ES6 draft (2016/1/11) 22.2.3.15 %TypedArray%.prototype.keys()
function TypedArrayKeys() {
// Step 1.
var O = this;
// Step 2-3.
// Step 2.
if (!IsObject(O) || !IsTypedArray(O)) {
return callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayKeys");
}
// Step 4-6. Bug 1101256: detachment checks
GetAttachedArrayBuffer(O);
// Step 7.
// Step 3.
return CreateArrayIterator(O, ITEM_KIND_KEY);
}
@@ -463,6 +519,8 @@ function TypedArrayLastIndexOf(searchElement, fromIndex = undefined) {
"TypedArrayLastIndexOf");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -473,8 +531,8 @@ function TypedArrayLastIndexOf(searchElement, fromIndex = undefined) {
if (len === 0)
return -1;
// Steps 7-8.
var n = fromIndex === undefined ? len - 1 : ToInteger(fromIndex);
// Steps 7-8. Add zero to convert -0 to +0, per ES6 5.2.
var n = fromIndex === undefined ? len - 1 : ToInteger(fromIndex) + 0;
// Steps 9-10.
var k = n >= 0 ? std_Math_min(n, len - 1) : len + n;
@@ -502,6 +560,8 @@ function TypedArrayMap(callbackfn, thisArg = undefined) {
"TypedArrayMap");
}
GetAttachedArrayBuffer(O);
// Step 4.
var len = TypedArrayLength(O);
@@ -541,6 +601,8 @@ function TypedArrayReduce(callbackfn/*, initialValue*/) {
if (!IsObject(this) || !IsTypedArray(this))
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduce");
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -580,6 +642,8 @@ function TypedArrayReduceRight(callbackfn/*, initialValue*/) {
if (!IsObject(this) || !IsTypedArray(this))
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduceRight");
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -620,6 +684,8 @@ function TypedArrayReverse() {
return callFunction(CallTypedArrayMethodIfWrapped, this, "TypedArrayReverse");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -651,37 +717,6 @@ function TypedArrayReverse() {
return O;
}
function ViewedArrayBufferIfReified(tarray) {
assert(IsTypedArray(tarray), "non-typed array asked for its buffer");
var buf = UnsafeGetReservedSlot(tarray, JS_TYPEDARRAYLAYOUT_BUFFER_SLOT);
assert(buf === null || (IsObject(buf) && (IsArrayBuffer(buf) || IsSharedArrayBuffer(buf))),
"unexpected value in buffer slot");
return buf;
}
function IsDetachedBuffer(buffer) {
// Typed arrays whose buffers are null use inline storage and can't have
// been neutered.
if (buffer === null)
return false;
assert(IsArrayBuffer(buffer) || IsSharedArrayBuffer(buffer),
"non-ArrayBuffer passed to IsDetachedBuffer");
// Typed arrays whose buffers map shared memory can't have been neutered.
//
// This check is more expensive than desirable, but IsDetachedBuffer is
// only hot for non-shared memory in SetFromNonTypedArray, so there is an
// optimization in place there to avoid incurring the cost here. An
// alternative is to give SharedArrayBuffer the same layout as ArrayBuffer.
if (IsSharedArrayBuffer(buffer))
return false;
var flags = UnsafeGetInt32FromReservedSlot(buffer, JS_ARRAYBUFFER_FLAGS_SLOT);
return (flags & JS_ARRAYBUFFER_NEUTERED_FLAG) !== 0;
}
// ES6 draft 20150220 22.2.3.22.1 %TypedArray%.prototype.set(array [, offset])
function SetFromNonTypedArray(target, array, targetOffset, targetLength, targetBuffer) {
assert(!IsPossiblyWrappedTypedArray(array),
@@ -789,9 +824,7 @@ function TypedArraySet(overloaded, offset) {
ThrowRangeError(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
// Steps 9-10.
var targetBuffer = ViewedArrayBufferIfReified(target);
if (IsDetachedBuffer(targetBuffer))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
var targetBuffer = GetAttachedArrayBuffer(target);
// Step 11.
var targetLength = TypedArrayLength(target);
@@ -814,6 +847,8 @@ function TypedArraySlice(start, end) {
return callFunction(CallTypedArrayMethodIfWrapped, O, start, end, "TypedArraySlice");
}
GetAttachedArrayBuffer(O);
// Step 4.
var len = TypedArrayLength(O);
@@ -870,6 +905,8 @@ function TypedArraySome(callbackfn, thisArg = undefined) {
"TypedArraySome");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -903,6 +940,74 @@ function TypedArraySome(callbackfn, thisArg = undefined) {
return false;
}
// ES6 draft 20151210 22.2.3.26
// Cases are ordered according to likelihood of occurrence
// as opposed to the ordering in the spec.
function TypedArrayCompare(x, y) {
// Step 1.
assert(typeof x === "number" && typeof y === "number",
"x and y are not numbers.");
// Steps 6 - 7.
var diff = x - y;
if (diff)
return diff;
// Steps 8 - 10.
if (x === 0 && y === 0)
return (1/x > 0 ? 1 : 0) - (1/y > 0 ? 1 : 0);
// Step 2. Implemented in TypedArraySort
// Step 3.
if (Number_isNaN(x) && Number_isNaN(y))
return 0;
// Steps 4 - 5.
if (Number_isNaN(x) || Number_isNaN(y))
return Number_isNaN(x) ? 1 : -1;
}
// ES6 draft 20151210 22.2.3.26 %TypedArray%.prototype.sort ( comparefn ).
function TypedArraySort(comparefn) {
// This function is not generic.
if (!IsObject(this) || !IsTypedArray(this)) {
return callFunction(CallTypedArrayMethodIfWrapped, this, comparefn,
"TypedArraySort");
}
// Step 1.
var obj = this;
// Step 2.
var buffer = GetAttachedArrayBuffer(obj);
// Step 3.
var len = TypedArrayLength(obj);
if (comparefn === undefined) {
comparefn = TypedArrayCompare;
} else {
// To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26
// the user supplied comparefn is wrapped.
var wrappedCompareFn = comparefn;
comparefn = function(x, y) {
// Step a.
var v = wrappedCompareFn(x, y);
// Step b.
if (IsDetachedBuffer(buffer))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
// Step c. is redundant, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1121937#c36
// Step d.
return v;
}
}
return QuickSort(obj, len, comparefn);
}
// ES6 draft 20150304 %TypedArray%.prototype.subarray
function TypedArraySubarray(begin, end) {
// Step 1.
@@ -959,7 +1064,8 @@ function TypedArrayValues() {
return callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayValues");
}
// Step 4-6. Bug 1101256: detachment checks
// Step 4-6.
GetAttachedArrayBuffer(O);
// Step 7.
return CreateArrayIterator(O, ITEM_KIND_VALUE);
@@ -975,6 +1081,8 @@ function TypedArrayIncludes(searchElement, fromIndex = 0) {
fromIndex, "TypedArrayIncludes");
}
GetAttachedArrayBuffer(this);
// Steps 1-2.
var O = this;
@@ -1167,3 +1275,79 @@ function TypedArrayStaticOf(/*...items*/) {
// Step 8.
return newObj;
}
// ES 2016 draft Dec 10, 2015 24.1.4.3.
function ArrayBufferSlice(start, end) {
// Step 1.
var O = this;
// Steps 2-3,
// This function is not generic.
if (!IsObject(O) || !IsArrayBuffer(O)) {
return callFunction(CallArrayBufferMethodIfWrapped, O, start, end,
"ArrayBufferSlice");
}
// Step 4.
if (IsDetachedBuffer(O))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
// Step 5.
var len = ArrayBufferByteLength(O);
// Step 6.
var relativeStart = ToInteger(start);
// Step 7.
var first = relativeStart < 0 ? std_Math_max(len + relativeStart, 0)
: std_Math_min(relativeStart, len);
// Step 8.
var relativeEnd = end === undefined ? len
: ToInteger(end);
// Step 9.
var final = relativeEnd < 0 ? std_Math_max(len + relativeEnd, 0)
: std_Math_min(relativeEnd, len);
// Step 10.
var newLen = std_Math_max(final - first, 0);
// Step 11
var ctor = SpeciesConstructor(O, GetBuiltinConstructor("ArrayBuffer"));
// Step 12.
var new_ = new ctor(newLen);
// Step 13.
if (!IsArrayBuffer(new_))
ThrowTypeError(JSMSG_NON_ARRAY_BUFFER_RETURNED);
// Step 14.
if (IsDetachedBuffer(new_))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
// Step 15.
if (new_ == O)
ThrowTypeError(JSMSG_SAME_ARRAY_BUFFER_RETURNED);
// Step 16.
if (ArrayBufferByteLength(new_) < newLen)
ThrowTypeError(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, newLen, ArrayBufferByteLength(new_));
// Step 18.
if (IsDetachedBuffer(O))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
// Steps 19-21.
ArrayBufferCopyData(new_, O, first | 0, newLen | 0);
// Step 22.
return new_;
}
function ArrayBufferStaticSlice(buf, start, end) {
if (arguments.length < 1)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'ArrayBuffer.slice');
return callFunction(ArrayBufferSlice, buf, start, end);
}
+30 -21
View File
@@ -412,32 +412,41 @@ js::ReferenceTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
* Note: these are partially defined in SIMD.cpp
*/
SimdType
SimdTypeDescr::type() const {
uint32_t t = uint32_t(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
MOZ_ASSERT(t < uint32_t(SimdType::Count));
return SimdType(t);
}
int32_t
SimdTypeDescr::size(Type t)
SimdTypeDescr::size(SimdType t)
{
MOZ_ASSERT(unsigned(t) <= SimdTypeDescr::Type::LAST_TYPE);
MOZ_ASSERT(unsigned(t) < unsigned(SimdType::Count));
switch (t) {
case SimdTypeDescr::Int8x16:
case SimdTypeDescr::Int16x8:
case SimdTypeDescr::Int32x4:
case SimdTypeDescr::Uint8x16:
case SimdTypeDescr::Uint16x8:
case SimdTypeDescr::Uint32x4:
case SimdTypeDescr::Float32x4:
case SimdTypeDescr::Float64x2:
case SimdTypeDescr::Bool8x16:
case SimdTypeDescr::Bool16x8:
case SimdTypeDescr::Bool32x4:
case SimdTypeDescr::Bool64x2:
case SimdType::Int8x16:
case SimdType::Int16x8:
case SimdType::Int32x4:
case SimdType::Uint8x16:
case SimdType::Uint16x8:
case SimdType::Uint32x4:
case SimdType::Float32x4:
case SimdType::Float64x2:
case SimdType::Bool8x16:
case SimdType::Bool16x8:
case SimdType::Bool32x4:
case SimdType::Bool64x2:
return 16;
case SimdType::Count:
break;
}
MOZ_CRASH("unexpected SIMD type");
}
int32_t
SimdTypeDescr::alignment(Type t)
SimdTypeDescr::alignment(SimdType t)
{
MOZ_ASSERT(unsigned(t) <= SimdTypeDescr::Type::LAST_TYPE);
MOZ_ASSERT(unsigned(t) < unsigned(SimdType::Count));
return size(t);
}
@@ -1377,7 +1386,7 @@ TypedObject::isAttached() const
if (table) {
JSObject* buffer = table->lookup(this);
if (buffer)
return !buffer->as<ArrayBufferObject>().isNeutered();
return !buffer->as<ArrayBufferObject>().isDetached();
}
return true;
}
@@ -1386,7 +1395,7 @@ TypedObject::isAttached() const
if (!as<OutlineTypedObject>().outOfLineTypedMem())
return false;
JSObject& owner = as<OutlineTypedObject>().owner();
if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isNeutered())
if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isDetached())
return false;
return true;
}
@@ -1399,7 +1408,7 @@ TypedObject::maybeForwardedIsAttached() const
if (!as<OutlineTypedObject>().outOfLineTypedMem())
return false;
JSObject& owner = *MaybeForwarded(&as<OutlineTypedObject>().owner());
if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isNeutered())
if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isDetached())
return false;
return true;
}
@@ -2093,7 +2102,7 @@ TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& proper
}
void
OutlineTypedObject::neuter(void* newData)
OutlineTypedObject::notifyBufferDetached(void* newData)
{
setData(reinterpret_cast<uint8_t*>(newData));
}
@@ -2347,7 +2356,7 @@ TypedObject::construct(JSContext* cx, unsigned int argc, Value* vp)
Rooted<ArrayBufferObject*> buffer(cx);
buffer = &args[0].toObject().as<ArrayBufferObject>();
if (callee->opaque() || buffer->isNeutered()) {
if (callee->opaque() || buffer->isDetached()) {
JS_ReportErrorNumber(cx, GetErrorMessage,
nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
return false;
+8 -27
View File
@@ -75,7 +75,7 @@
* which can be either an array buffer or an inline typed object. Outline
* typed objects may be attached or unattached. An unattached typed object
* has no data associated with it. When first created, objects are always
* attached, but they can become unattached if their buffer is neutered.
* attached, but they can become unattached if their buffer becomes detached.
*
* Note that whether a typed object is opaque is not directly
* connected to its type. That is, opaque types are *always*
@@ -327,42 +327,23 @@ class ComplexTypeDescr : public TypeDescr
}
};
enum class SimdType : uint8_t;
/*
* SIMD Type descriptors.
*/
class SimdTypeDescr : public ComplexTypeDescr
{
public:
enum Type {
Int8x16 = JS_SIMDTYPEREPR_INT8X16,
Int16x8 = JS_SIMDTYPEREPR_INT16X8,
Int32x4 = JS_SIMDTYPEREPR_INT32X4,
Uint8x16 = JS_SIMDTYPEREPR_UINT8X16,
Uint16x8 = JS_SIMDTYPEREPR_UINT16X8,
Uint32x4 = JS_SIMDTYPEREPR_UINT32X4,
Float32x4 = JS_SIMDTYPEREPR_FLOAT32X4,
Float64x2 = JS_SIMDTYPEREPR_FLOAT64X2,
Bool8x16 = JS_SIMDTYPEREPR_BOOL8X16,
Bool16x8 = JS_SIMDTYPEREPR_BOOL16X8,
Bool32x4 = JS_SIMDTYPEREPR_BOOL32X4,
Bool64x2 = JS_SIMDTYPEREPR_BOOL64X2,
LAST_TYPE = Bool64x2
};
static const type::Kind Kind = type::Simd;
static const bool Opaque = false;
static const Class class_;
static int32_t size(Type t);
static int32_t alignment(Type t);
SimdTypeDescr::Type type() const {
uint32_t t = uint32_t(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
MOZ_ASSERT(t <= LAST_TYPE);
return SimdTypeDescr::Type(t);
}
static int32_t size(SimdType t);
static int32_t alignment(SimdType t);
static bool call(JSContext* cx, unsigned argc, Value* vp);
static bool is(const Value& v);
SimdType type() const;
};
bool IsTypedObjectClass(const Class* clasp); // Defined below
@@ -666,7 +647,7 @@ class OutlineTypedObject : public TypedObject
void attach(JSContext* cx, TypedObject& typedObj, int32_t offset);
// Invoked when array buffer is transferred elsewhere
void neuter(void* newData);
void notifyBufferDetached(void* newData);
static void obj_trace(JSTracer* trace, JSObject* object);
};

Some files were not shown because too many files have changed in this diff Show More