mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
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:
@@ -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");
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1555,6 +1555,9 @@ nsFrameLoader::DestroyComplete()
|
||||
if (mChildMessageManager) {
|
||||
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
|
||||
}
|
||||
|
||||
mMessageManager = nullptr;
|
||||
mChildMessageManager = nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" %
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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>
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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,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();
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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>
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -598,7 +598,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static ThreadLocal<EGLContext> sCurrentContext;
|
||||
static MOZ_THREAD_LOCAL(EGLContext) sCurrentContext;
|
||||
public:
|
||||
|
||||
#else
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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(); }
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace JS {
|
||||
_(ArrayDoubleConversion) \
|
||||
_(ArrayRange) \
|
||||
_(ArraySeenNegativeIndex) \
|
||||
_(TypedObjectNeutered) \
|
||||
_(TypedObjectHasDetachedBuffer) \
|
||||
_(TypedObjectArrayRange) \
|
||||
_(AccessNotDense) \
|
||||
_(AccessNotSimdObject) \
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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)";
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+861
-913
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user