зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team. a=merge
This commit is contained in:
Коммит
0040548882
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b4f6fd4afd03161f53c7d2a663750f94762bd238"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "c2582f4be03cd12124b96a263c8d14c774f0ffe4",
|
||||
"git_revision": "b75979ec8862bd5799a7c42e938d3f67be38d6ae",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "a330673cd99b4f50094fb7dd14000efdb9cf81b4",
|
||||
"revision": "f919c5b5f0f0b6fd3ef3346850710edafa2a615b",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b4f6fd4afd03161f53c7d2a663750f94762bd238"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2582f4be03cd12124b96a263c8d14c774f0ffe4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b75979ec8862bd5799a7c42e938d3f67be38d6ae"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -5458,23 +5458,30 @@
|
|||
// to get a full-resolution drag image for use on HiDPI displays.
|
||||
let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
|
||||
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
let canvas = this._dndCanvas ? this._dndCanvas
|
||||
: document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas.mozOpaque = true;
|
||||
canvas.width = 160 * scale;
|
||||
canvas.height = 90 * scale;
|
||||
let toDrag;
|
||||
if (gMultiProcessBrowser) {
|
||||
var context = canvas.getContext('2d');
|
||||
context.fillStyle = "white";
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
// Create a panel to use it in setDragImage
|
||||
// which will tell xul to render a panel that follows
|
||||
// the pointer while a dnd session is on.
|
||||
var panel = document.createElement("panel");
|
||||
panel.setAttribute("type", "drag");
|
||||
panel.appendChild(canvas);
|
||||
document.documentElement.appendChild(panel);
|
||||
if (!this._dndPanel) {
|
||||
this._dndCanvas = canvas;
|
||||
this._dndPanel = document.createElement("panel");
|
||||
this._dndPanel.setAttribute("type", "drag");
|
||||
this._dndPanel.appendChild(canvas);
|
||||
document.documentElement.appendChild(this._dndPanel);
|
||||
}
|
||||
// PageThumb is async with e10s but that's fine
|
||||
// since we can update the panel during the dnd.
|
||||
PageThumbs.captureToCanvas(browser, canvas);
|
||||
toDrag = panel;
|
||||
toDrag = this._dndPanel;
|
||||
} else {
|
||||
// For the non e10s case we can just use PageThumbs
|
||||
// sync. No need for xul magic, the native dnd will
|
||||
|
|
|
@ -50,7 +50,7 @@ nsEdgeReadingListExtractor::Extract(const nsAString& aDBPath, nsIArray** aItems)
|
|||
// the right things
|
||||
bool instanceCreated, sessionCreated, dbOpened, tableOpened;
|
||||
|
||||
char16_t* dbPath = ToNewUnicode(aDBPath);
|
||||
char16ptr_t dbPath = ToNewUnicode(aDBPath);
|
||||
|
||||
// Check for the right page size and initialize with that
|
||||
unsigned long pageSize;
|
||||
|
|
|
@ -103,17 +103,23 @@ AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (aWindow) {
|
||||
nsCOMPtr<nsPIDOMWindow> pInnerWindow = do_QueryInterface(aWindow);
|
||||
MOZ_ASSERT(pInnerWindow->IsInnerWindow());
|
||||
mInnerWindowID = pInnerWindow->WindowID();
|
||||
if (NS_WARN_IF(!aWindow)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> topWindow;
|
||||
aWindow->GetScriptableTop(getter_AddRefs(topWindow));
|
||||
mWindow = do_QueryInterface(topWindow);
|
||||
if (mWindow) {
|
||||
mWindow = mWindow->GetOuterWindow();
|
||||
}
|
||||
nsCOMPtr<nsPIDOMWindow> pInnerWindow = do_QueryInterface(aWindow);
|
||||
MOZ_ASSERT(pInnerWindow->IsInnerWindow());
|
||||
mInnerWindowID = pInnerWindow->WindowID();
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> topWindow;
|
||||
aWindow->GetScriptableTop(getter_AddRefs(topWindow));
|
||||
mWindow = do_QueryInterface(topWindow);
|
||||
if (mWindow) {
|
||||
mWindow = mWindow->GetOuterWindow();
|
||||
}
|
||||
|
||||
if (!mWindow) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mAudioChannelType = aChannelType;
|
||||
|
@ -134,6 +140,13 @@ NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(uint32_t aNotifyPlayback,
|
|||
MOZ_ASSERT(aVolume);
|
||||
MOZ_ASSERT(aMuted);
|
||||
|
||||
// Window-less AudioChannelAgents are muted by default.
|
||||
if (!mWindow) {
|
||||
*aVolume = 0;
|
||||
*aMuted = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
|
||||
if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
|
||||
service == nullptr || mIsRegToService) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "nsAttrAndChildArray.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
|
@ -23,6 +24,8 @@
|
|||
#include "nsAutoPtr.h"
|
||||
#include "nsContentUtils.h" // nsAutoScriptBlocker
|
||||
|
||||
using mozilla::CheckedUint32;
|
||||
|
||||
/*
|
||||
CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer.
|
||||
It should be small enough to not cause collisions between adjecent arrays, and
|
||||
|
|
|
@ -1591,6 +1591,18 @@ nsIDocument::~nsIDocument()
|
|||
UnlinkOriginalDocumentIfStatic();
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::IsAboutPage()
|
||||
{
|
||||
nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
principal->GetURI(getter_AddRefs(uri));
|
||||
bool isAboutScheme = true;
|
||||
if (uri) {
|
||||
uri->SchemeIs("about", &isAboutScheme);
|
||||
}
|
||||
return isAboutScheme;
|
||||
}
|
||||
|
||||
nsDocument::~nsDocument()
|
||||
{
|
||||
|
@ -1608,15 +1620,7 @@ nsDocument::~nsDocument()
|
|||
|
||||
if (IsTopLevelContentDocument()) {
|
||||
//don't report for about: pages
|
||||
nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
principal->GetURI(getter_AddRefs(uri));
|
||||
bool isAboutScheme = true;
|
||||
if (uri) {
|
||||
uri->SchemeIs("about", &isAboutScheme);
|
||||
}
|
||||
|
||||
if (!isAboutScheme) {
|
||||
if (!IsAboutPage()) {
|
||||
// Record the page load
|
||||
uint32_t pageLoaded = 1;
|
||||
Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
|
||||
|
@ -7900,9 +7904,10 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
|
|||
|
||||
CSSToScreenScale defaultScale = layoutDeviceScale
|
||||
* LayoutDeviceToScreenScale(1.0);
|
||||
// Get requested Desktopmode
|
||||
|
||||
// Special behaviour for desktop mode, provided we are not on an about: page
|
||||
nsPIDOMWindow* win = GetWindow();
|
||||
if (win && win->IsDesktopModeViewport())
|
||||
if (win && win->IsDesktopModeViewport() && !IsAboutPage())
|
||||
{
|
||||
float viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
|
||||
float scaleToFit = aDisplaySize.width / viewportWidth;
|
||||
|
|
|
@ -1770,6 +1770,9 @@ private:
|
|||
// requestAnimationFrame, if it's OK to do so.
|
||||
void MaybeRescheduleAnimationFrameNotifications();
|
||||
|
||||
// Returns true if the scheme for the url for this document is "about"
|
||||
bool IsAboutPage();
|
||||
|
||||
// These are not implemented and not supported.
|
||||
nsDocument(const nsDocument& aOther);
|
||||
nsDocument& operator=(const nsDocument& aOther);
|
||||
|
|
|
@ -23,7 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=416317
|
|||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 416317 **/
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
SimpleTest.requestLongerTimeout(3);
|
||||
// Subframe handles the test
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "pk11pub.h"
|
||||
#include "cryptohi.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "mozilla/dom/CryptoKey.h"
|
||||
#include "mozilla/dom/WebCryptoCommon.h"
|
||||
|
@ -1249,6 +1250,11 @@ CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Ensure that NSS is initialized.
|
||||
if (!EnsureNSSInitializedChromeOrContent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t version;
|
||||
CryptoBuffer sym, priv, pub;
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ FINAL_LIBRARY = 'xul'
|
|||
|
||||
LOCAL_INCLUDES += [
|
||||
'/security/manager/ssl',
|
||||
'/security/pkix/include',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1188750 - WebCrypto must ensure NSS is initialized before deserializing</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
let db;
|
||||
|
||||
function err(resolve) {
|
||||
return e => resolve(e.target.error.message);
|
||||
}
|
||||
|
||||
function openDatabase() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = indexedDB.open("keystore", 1);
|
||||
|
||||
request.onerror = err(reject);
|
||||
request.onsuccess = function (event) {
|
||||
db = event.target.result;
|
||||
resolve();
|
||||
};
|
||||
|
||||
request.onupgradeneeded = function(event) {
|
||||
db = event.target.result;
|
||||
let objectStore = db.createObjectStore("keys", {autoIncrement: true});
|
||||
objectStore.transaction.oncomplete = resolve;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function storeKey(key) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let transaction = db.transaction("keys", "readwrite");
|
||||
transaction.objectStore("keys").put(key, key.type);
|
||||
|
||||
transaction.onabort = err(reject);
|
||||
transaction.onerror = err(reject);
|
||||
|
||||
transaction.oncomplete = function () {
|
||||
resolve(key);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function retrieveKey() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let transaction = db.transaction("keys", "readonly");
|
||||
let cursor = transaction.objectStore("keys").openCursor();
|
||||
|
||||
cursor.onerror = err(reject);
|
||||
cursor.onabort = err(reject);
|
||||
|
||||
cursor.onsuccess = function (event) {
|
||||
try {
|
||||
let result = event.target.result;
|
||||
resolve(result && result.value);
|
||||
} catch (e) {
|
||||
reject(e.message);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function generateKey() {
|
||||
let algorithm = {
|
||||
name: "RSASSA-PKCS1-v1_5",
|
||||
hash: "SHA-256",
|
||||
modulusLength: 1024,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01])
|
||||
};
|
||||
|
||||
return crypto.subtle.generateKey(algorithm, true, ["sign", "verify"]);
|
||||
}
|
||||
|
||||
openDatabase()
|
||||
.then(retrieveKey).then(generateKey).then(storeKey)
|
||||
.then(() => alert("ok")).catch(alert);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -2,11 +2,14 @@
|
|||
# Bug 1010743 - Re-enable WebCrypto tests on b2g
|
||||
skip-if = (buildapp == 'b2g')
|
||||
support-files =
|
||||
file_indexedDB.html
|
||||
test-array.js
|
||||
test-vectors.js
|
||||
test_WebCrypto.css
|
||||
util.js
|
||||
|
||||
[test_indexedDB.html]
|
||||
skip-if = toolkit == 'android' # bug 1200570
|
||||
[test_WebCrypto.html]
|
||||
[test_WebCrypto_DH.html]
|
||||
[test_WebCrypto_ECDH.html]
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1188750 - WebCrypto must ensure NSS is initialized before deserializing</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"?>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
/*
|
||||
* Bug 1188750 - The WebCrypto API must ensure that NSS was initialized
|
||||
* for the current process before trying to deserialize objects like
|
||||
* CryptoKeys from e.g. IndexedDB.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = "http://www.example.com/tests/" +
|
||||
"dom/crypto/test/file_indexedDB.html";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function createMozBrowserFrame(cb) {
|
||||
let frame = document.createElement("iframe");
|
||||
SpecialPowers.wrap(frame).mozbrowser = true;
|
||||
frame.src = TEST_URI;
|
||||
|
||||
frame.addEventListener("mozbrowsershowmodalprompt", function onPrompt(e) {
|
||||
frame.removeEventListener("mozbrowsershowmodalprompt", onPrompt);
|
||||
cb(frame, e.detail.message);
|
||||
});
|
||||
|
||||
document.body.appendChild(frame);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// Load the test app once, to generate and store keys.
|
||||
createMozBrowserFrame((frame, result) => {
|
||||
is(result, "ok", "stored keys successfully");
|
||||
frame.remove();
|
||||
|
||||
// Load the test app again to retrieve stored keys.
|
||||
createMozBrowserFrame((frame, result) => {
|
||||
is(result, "ok", "retrieved keys successfully");
|
||||
frame.remove();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener("load", function () {
|
||||
SpecialPowers.addPermission("browser", true, document);
|
||||
SpecialPowers.pushPrefEnv({set: [
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.mozBrowserFramesEnabled", true]
|
||||
]}, runTest);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -206,8 +206,9 @@ InternalRequest::MapContentPolicyTypeToRequestContext(nsContentPolicyType aConte
|
|||
return context;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InternalRequest::IsNavigationRequest() const
|
||||
InternalRequest::IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
// https://fetch.spec.whatwg.org/#navigation-request-context
|
||||
//
|
||||
|
@ -215,17 +216,22 @@ InternalRequest::IsNavigationRequest() const
|
|||
// "iframe", "internal" (as long as context frame type is not "none"),
|
||||
// "location", "metarefresh", and "prerender".
|
||||
//
|
||||
// TODO: include equivalent check for "form" context
|
||||
// TODO: include equivalent check for "prerender" context
|
||||
return mContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
mContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
|
||||
mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
|
||||
mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
|
||||
mContentPolicyType == nsIContentPolicy::TYPE_REFRESH;
|
||||
// Note, all of these request types are effectively initiated by nsDocShell.
|
||||
//
|
||||
// The TYPE_REFRESH is used in some code paths for metarefresh, but will not
|
||||
// be seen during the actual load. Instead the new load gets a normal
|
||||
// nsDocShell policy type. We include it here in case this utility method
|
||||
// is called before the load starts.
|
||||
return aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_REFRESH;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InternalRequest::IsWorkerRequest() const
|
||||
InternalRequest::IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
// https://fetch.spec.whatwg.org/#worker-request-context
|
||||
//
|
||||
|
@ -235,8 +241,20 @@ InternalRequest::IsWorkerRequest() const
|
|||
// Note, service workers are not included here because currently there is
|
||||
// no way to generate a Request with a "serviceworker" RequestContext.
|
||||
// ServiceWorker scripts cannot be intercepted.
|
||||
return mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
|
||||
return aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
|
||||
}
|
||||
|
||||
bool
|
||||
InternalRequest::IsNavigationRequest() const
|
||||
{
|
||||
return IsNavigationContentPolicy(mContentPolicyType);
|
||||
}
|
||||
|
||||
bool
|
||||
InternalRequest::IsWorkerRequest() const
|
||||
{
|
||||
return IsWorkerContentPolicy(mContentPolicyType);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -245,5 +263,63 @@ InternalRequest::IsClientRequest() const
|
|||
return IsNavigationRequest() || IsWorkerRequest();
|
||||
}
|
||||
|
||||
// static
|
||||
RequestMode
|
||||
InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel)
|
||||
{
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aChannel->GetLoadInfo(getter_AddRefs(loadInfo))));
|
||||
|
||||
// RequestMode deviates from our internal security mode for navigations.
|
||||
// While navigations normally allow cross origin we must set a same-origin
|
||||
// RequestMode to get the correct service worker interception restrictions
|
||||
// in place.
|
||||
// TODO: remove the worker override once securityMode is fully implemented (bug 1189945)
|
||||
nsContentPolicyType contentPolicy = loadInfo->InternalContentPolicyType();
|
||||
if (IsNavigationContentPolicy(contentPolicy) ||
|
||||
IsWorkerContentPolicy(contentPolicy)) {
|
||||
return RequestMode::Same_origin;
|
||||
}
|
||||
|
||||
uint32_t securityMode;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSecurityMode(&securityMode)));
|
||||
|
||||
switch(securityMode) {
|
||||
case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS:
|
||||
case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED:
|
||||
return RequestMode::Same_origin;
|
||||
case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS:
|
||||
case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL:
|
||||
return RequestMode::No_cors;
|
||||
case nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS:
|
||||
// TODO: Check additional flag force-preflight after bug 1199693 (bug 1189945)
|
||||
return RequestMode::Cors;
|
||||
default:
|
||||
// TODO: assert never reached after CorsMode flag removed (bug 1189945)
|
||||
MOZ_ASSERT(securityMode == nsILoadInfo::SEC_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: remove following code once securityMode is fully implemented (bug 1189945)
|
||||
|
||||
// We only support app:// protocol interception in non-release builds.
|
||||
#ifndef RELEASE_BUILD
|
||||
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aChannel);
|
||||
if (jarChannel) {
|
||||
return RequestMode::No_cors;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
|
||||
|
||||
uint32_t corsMode;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(httpChannel->GetCorsMode(&corsMode)));
|
||||
|
||||
// This cast is valid due to static asserts in ServiceWorkerManager.cpp.
|
||||
return static_cast<RequestMode>(corsMode);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -377,6 +377,8 @@ public:
|
|||
bool
|
||||
IsClientRequest() const;
|
||||
|
||||
static RequestMode
|
||||
MapChannelToRequestMode(nsIChannel* aChannel);
|
||||
|
||||
private:
|
||||
// Does not copy mBodyStream. Use fallible Clone() for complete copy.
|
||||
|
@ -387,6 +389,12 @@ private:
|
|||
static RequestContext
|
||||
MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
static bool
|
||||
IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
static bool
|
||||
IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
nsCString mMethod;
|
||||
// mURL always stores the url with the ref stripped
|
||||
nsCString mURL;
|
||||
|
|
|
@ -130,7 +130,10 @@ public:
|
|||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
if (mShouldNotify) {
|
||||
mElement->NotifyAudioChannelAgent(false);
|
||||
// The audio channel agent may not exist now.
|
||||
if (mElement->MaybeCreateAudioChannelAgent()) {
|
||||
mElement->NotifyAudioChannelAgent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
~AutoNotifyAudioChannelAgent()
|
||||
|
|
|
@ -18,6 +18,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549475
|
|||
</div>
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
|
||||
/**
|
||||
* This files tests the 'value sanitization algorithm' for the various input
|
||||
* types. Note that an input's value is affected by more than just its type's
|
||||
|
|
|
@ -15,79 +15,4 @@ GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2)
|
|||
return std::max(aChannels1, aChannels2);
|
||||
}
|
||||
|
||||
/**
|
||||
* UpMixMatrix represents a conversion matrix by exploiting the fact that
|
||||
* each output channel comes from at most one input channel.
|
||||
*/
|
||||
struct UpMixMatrix {
|
||||
uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
|
||||
};
|
||||
|
||||
static const UpMixMatrix
|
||||
gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
|
||||
{
|
||||
// Upmixes from mono
|
||||
{ { 0, 0 } },
|
||||
{ { 0, IGNORE, IGNORE } },
|
||||
{ { 0, 0, IGNORE, IGNORE } },
|
||||
{ { 0, IGNORE, IGNORE, IGNORE, IGNORE } },
|
||||
{ { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } },
|
||||
// Upmixes from stereo
|
||||
{ { 0, 1, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } },
|
||||
// Upmixes from 3-channel
|
||||
{ { 0, 1, 2, IGNORE } },
|
||||
{ { 0, 1, 2, IGNORE, IGNORE } },
|
||||
{ { 0, 1, 2, IGNORE, IGNORE, IGNORE } },
|
||||
// Upmixes from quad
|
||||
{ { 0, 1, 2, 3, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE, 2, 3 } },
|
||||
// Upmixes from 5-channel
|
||||
{ { 0, 1, 2, 3, 4, IGNORE } }
|
||||
};
|
||||
|
||||
void
|
||||
AudioChannelsUpMix(nsTArray<const void*>* aChannelArray,
|
||||
uint32_t aOutputChannelCount,
|
||||
const void* aZeroChannel)
|
||||
{
|
||||
uint32_t inputChannelCount = aChannelArray->Length();
|
||||
uint32_t outputChannelCount =
|
||||
GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount);
|
||||
NS_ASSERTION(outputChannelCount > inputChannelCount,
|
||||
"No up-mix needed");
|
||||
MOZ_ASSERT(inputChannelCount > 0, "Bad number of channels");
|
||||
MOZ_ASSERT(outputChannelCount > 0, "Bad number of channels");
|
||||
|
||||
aChannelArray->SetLength(outputChannelCount);
|
||||
|
||||
if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS &&
|
||||
outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) {
|
||||
const UpMixMatrix& m = gUpMixMatrices[
|
||||
gMixingMatrixIndexByChannels[inputChannelCount - 1] +
|
||||
outputChannelCount - inputChannelCount - 1];
|
||||
|
||||
const void* outputChannels[CUSTOM_CHANNEL_LAYOUTS];
|
||||
|
||||
for (uint32_t i = 0; i < outputChannelCount; ++i) {
|
||||
uint8_t channelIndex = m.mInputDestination[i];
|
||||
if (channelIndex == IGNORE) {
|
||||
outputChannels[i] = aZeroChannel;
|
||||
} else {
|
||||
outputChannels[i] = aChannelArray->ElementAt(channelIndex);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < outputChannelCount; ++i) {
|
||||
aChannelArray->ElementAt(i) = outputChannels[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
|
||||
aChannelArray->ElementAt(i) = aZeroChannel;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -58,24 +58,6 @@ const int gMixingMatrixIndexByChannels[CUSTOM_CHANNEL_LAYOUTS - 1] =
|
|||
uint32_t
|
||||
GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2);
|
||||
|
||||
/**
|
||||
* Given an array of input channel data, and an output channel count,
|
||||
* replaces the array with an array of upmixed channels.
|
||||
* This shuffles the array and may set some channel buffers to aZeroChannel.
|
||||
* Don't call this with input count >= output count.
|
||||
* This may return *more* channels than requested. In that case, downmixing
|
||||
* is required to to get to aOutputChannelCount. (This is how we handle
|
||||
* odd cases like 3 -> 4 upmixing.)
|
||||
* If aChannelArray.Length() was the input to one of a series of
|
||||
* GetAudioChannelsSuperset calls resulting in aOutputChannelCount,
|
||||
* no downmixing will be required.
|
||||
*/
|
||||
void
|
||||
AudioChannelsUpMix(nsTArray<const void*>* aChannelArray,
|
||||
uint32_t aOutputChannelCount,
|
||||
const void* aZeroChannel);
|
||||
|
||||
|
||||
/**
|
||||
* DownMixMatrix represents a conversion matrix efficiently by exploiting the
|
||||
* fact that each input channel contributes to at most one output channel,
|
||||
|
@ -124,19 +106,19 @@ gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
|
|||
* input count <= output count.
|
||||
*/
|
||||
template<typename T>
|
||||
void AudioChannelsDownMix(const nsTArray<const void*>& aChannelArray,
|
||||
T** aOutputChannels,
|
||||
uint32_t aOutputChannelCount,
|
||||
uint32_t aDuration)
|
||||
void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
T** aOutputChannels,
|
||||
uint32_t aOutputChannelCount,
|
||||
uint32_t aDuration)
|
||||
{
|
||||
uint32_t inputChannelCount = aChannelArray.Length();
|
||||
const void* const* inputChannels = aChannelArray.Elements();
|
||||
const T* const* inputChannels = aChannelArray.Elements();
|
||||
NS_ASSERTION(inputChannelCount > aOutputChannelCount, "Nothing to do");
|
||||
|
||||
if (inputChannelCount > 6) {
|
||||
// Just drop the unknown channels.
|
||||
for (uint32_t o = 0; o < aOutputChannelCount; ++o) {
|
||||
memcpy(aOutputChannels[o], inputChannels[o], aDuration*sizeof(T));
|
||||
PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -153,8 +135,7 @@ void AudioChannelsDownMix(const nsTArray<const void*>& aChannelArray,
|
|||
for (uint32_t s = 0; s < aDuration; ++s) {
|
||||
// Reserve an extra junk channel at the end for the cases where we
|
||||
// want an input channel to contribute to nothing
|
||||
T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1];
|
||||
memset(outputChannels, 0, sizeof(T)*(CUSTOM_CHANNEL_LAYOUTS));
|
||||
T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1] = {0};
|
||||
for (uint32_t c = 0; c < inputChannelCount; ++c) {
|
||||
outputChannels[m.mInputDestination[c]] +=
|
||||
m.mInputCoefficient[c]*(static_cast<const T*>(inputChannels[c]))[s];
|
||||
|
@ -171,6 +152,94 @@ void AudioChannelsDownMix(const nsTArray<const void*>& aChannelArray,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UpMixMatrix represents a conversion matrix by exploiting the fact that
|
||||
* each output channel comes from at most one input channel.
|
||||
*/
|
||||
struct UpMixMatrix {
|
||||
uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
|
||||
};
|
||||
|
||||
static const UpMixMatrix
|
||||
gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
|
||||
{
|
||||
// Upmixes from mono
|
||||
{ { 0, 0 } },
|
||||
{ { 0, IGNORE, IGNORE } },
|
||||
{ { 0, 0, IGNORE, IGNORE } },
|
||||
{ { 0, IGNORE, IGNORE, IGNORE, IGNORE } },
|
||||
{ { IGNORE, IGNORE, 0, IGNORE, IGNORE, IGNORE } },
|
||||
// Upmixes from stereo
|
||||
{ { 0, 1, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE, IGNORE, IGNORE } },
|
||||
// Upmixes from 3-channel
|
||||
{ { 0, 1, 2, IGNORE } },
|
||||
{ { 0, 1, 2, IGNORE, IGNORE } },
|
||||
{ { 0, 1, 2, IGNORE, IGNORE, IGNORE } },
|
||||
// Upmixes from quad
|
||||
{ { 0, 1, 2, 3, IGNORE } },
|
||||
{ { 0, 1, IGNORE, IGNORE, 2, 3 } },
|
||||
// Upmixes from 5-channel
|
||||
{ { 0, 1, 2, 3, 4, IGNORE } }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Given an array of input channel data, and an output channel count,
|
||||
* replaces the array with an array of upmixed channels.
|
||||
* This shuffles the array and may set some channel buffers to aZeroChannel.
|
||||
* Don't call this with input count >= output count.
|
||||
* This may return *more* channels than requested. In that case, downmixing
|
||||
* is required to to get to aOutputChannelCount. (This is how we handle
|
||||
* odd cases like 3 -> 4 upmixing.)
|
||||
* If aChannelArray.Length() was the input to one of a series of
|
||||
* GetAudioChannelsSuperset calls resulting in aOutputChannelCount,
|
||||
* no downmixing will be required.
|
||||
*/
|
||||
template<typename T>
|
||||
void
|
||||
AudioChannelsUpMix(nsTArray<const T*>* aChannelArray,
|
||||
uint32_t aOutputChannelCount,
|
||||
const T* aZeroChannel)
|
||||
{
|
||||
uint32_t inputChannelCount = aChannelArray->Length();
|
||||
uint32_t outputChannelCount =
|
||||
GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount);
|
||||
NS_ASSERTION(outputChannelCount > inputChannelCount,
|
||||
"No up-mix needed");
|
||||
MOZ_ASSERT(inputChannelCount > 0, "Bad number of channels");
|
||||
MOZ_ASSERT(outputChannelCount > 0, "Bad number of channels");
|
||||
|
||||
aChannelArray->SetLength(outputChannelCount);
|
||||
|
||||
if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS &&
|
||||
outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) {
|
||||
const UpMixMatrix& m = gUpMixMatrices[
|
||||
gMixingMatrixIndexByChannels[inputChannelCount - 1] +
|
||||
outputChannelCount - inputChannelCount - 1];
|
||||
|
||||
const T* outputChannels[CUSTOM_CHANNEL_LAYOUTS];
|
||||
|
||||
for (uint32_t i = 0; i < outputChannelCount; ++i) {
|
||||
uint8_t channelIndex = m.mInputDestination[i];
|
||||
if (channelIndex == IGNORE) {
|
||||
outputChannels[i] = aZeroChannel;
|
||||
} else {
|
||||
outputChannels[i] = aChannelArray->ElementAt(channelIndex);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < outputChannelCount; ++i) {
|
||||
aChannelArray->ElementAt(i) = outputChannels[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
|
||||
aChannelArray->ElementAt(i) = aZeroChannel;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* 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 AudioPacketizer_h_
|
||||
#define AudioPacketizer_h_
|
||||
|
||||
#include <mozilla/PodOperations.h>
|
||||
#include <mozilla/Assertions.h>
|
||||
#include <nsAutoPtr.h>
|
||||
#include <AudioSampleFormat.h>
|
||||
|
||||
// Enable this to warn when `Output` has been called but not enough data was
|
||||
// buffered.
|
||||
// #define LOG_PACKETIZER_UNDERRUN
|
||||
|
||||
namespace mozilla {
|
||||
/**
|
||||
* This class takes arbitrary input data, and returns packets of a specific
|
||||
* size. In the process, it can convert audio samples from 16bit integers to
|
||||
* float (or vice-versa).
|
||||
*
|
||||
* Input and output, as well as length units in the public interface are
|
||||
* interleaved frames.
|
||||
*
|
||||
* Allocations of output buffer can be performed by this class. Buffers can
|
||||
* simply be delete-d. This is because packets are intended to be sent off to
|
||||
* non-gecko code using normal pointers/length pairs
|
||||
*
|
||||
* Alternatively, consumers can pass in a buffer in which the output is copied.
|
||||
* The buffer needs to be large enough to store a packet worth of audio.
|
||||
*
|
||||
* The implementation uses a circular buffer using absolute virtual indices.
|
||||
*/
|
||||
template <typename InputType, typename OutputType>
|
||||
class AudioPacketizer
|
||||
{
|
||||
public:
|
||||
AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels)
|
||||
: mPacketSize(aPacketSize)
|
||||
, mChannels(aChannels)
|
||||
, mReadIndex(0)
|
||||
, mWriteIndex(0)
|
||||
// Start off with a single packet
|
||||
, mStorage(new InputType[aPacketSize * aChannels])
|
||||
, mLength(aPacketSize * aChannels)
|
||||
{
|
||||
MOZ_ASSERT(aPacketSize > 0 && aChannels > 0,
|
||||
"The packet size and the number of channel should be strictly positive");
|
||||
}
|
||||
|
||||
void Input(const InputType* aFrames, uint32_t aFrameCount)
|
||||
{
|
||||
uint32_t inputSamples = aFrameCount * mChannels;
|
||||
// Need to grow the storage. This should rarely happen, if at all, once the
|
||||
// array has the right size.
|
||||
if (inputSamples > EmptySlots()) {
|
||||
// Calls to Input and Output are roughtly interleaved
|
||||
// (Input,Output,Input,Output, etc.), or balanced
|
||||
// (Input,Input,Input,Output,Output,Output), so we update the buffer to
|
||||
// the exact right size in order to not waste space.
|
||||
uint32_t newLength = AvailableSamples() + inputSamples;
|
||||
uint32_t toCopy = AvailableSamples();
|
||||
nsAutoPtr<InputType> oldStorage = mStorage;
|
||||
mStorage = new InputType[newLength];
|
||||
// Copy the old data at the beginning of the new storage.
|
||||
if (WriteIndex() >= ReadIndex()) {
|
||||
PodCopy(mStorage.get(),
|
||||
oldStorage.get() + ReadIndex(),
|
||||
AvailableSamples());
|
||||
} else {
|
||||
uint32_t firstPartLength = mLength - ReadIndex();
|
||||
uint32_t secondPartLength = AvailableSamples() - firstPartLength;
|
||||
PodCopy(mStorage.get(),
|
||||
oldStorage.get() + ReadIndex(),
|
||||
firstPartLength);
|
||||
PodCopy(mStorage.get() + firstPartLength,
|
||||
oldStorage.get(),
|
||||
secondPartLength);
|
||||
}
|
||||
mWriteIndex = toCopy;
|
||||
mReadIndex = 0;
|
||||
mLength = newLength;
|
||||
}
|
||||
|
||||
if (WriteIndex() + inputSamples <= mLength) {
|
||||
PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels);
|
||||
} else {
|
||||
uint32_t firstPartLength = mLength - WriteIndex();
|
||||
uint32_t secondPartLength = inputSamples - firstPartLength;
|
||||
PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength);
|
||||
PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength);
|
||||
}
|
||||
|
||||
mWriteIndex += inputSamples;
|
||||
}
|
||||
|
||||
OutputType* Output()
|
||||
{
|
||||
uint32_t samplesNeeded = mPacketSize * mChannels;
|
||||
OutputType* out = new OutputType[samplesNeeded];
|
||||
|
||||
Output(out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void Output(OutputType* aOutputBuffer)
|
||||
{
|
||||
uint32_t samplesNeeded = mPacketSize * mChannels;
|
||||
|
||||
// Under-run. Pad the end of the buffer with silence.
|
||||
if (AvailableSamples() < samplesNeeded) {
|
||||
#ifdef LOG_PACKETIZER_UNDERRUN
|
||||
char buf[256];
|
||||
snprintf(buf, 256,
|
||||
"AudioPacketizer %p underrun: available: %u, needed: %u\n",
|
||||
this, AvailableSamples(), samplesNeeded);
|
||||
NS_WARNING(buf);
|
||||
#endif
|
||||
uint32_t zeros = samplesNeeded - AvailableSamples();
|
||||
PodZero(aOutputBuffer + AvailableSamples(), zeros);
|
||||
samplesNeeded -= zeros;
|
||||
}
|
||||
if (ReadIndex() + samplesNeeded <= mLength) {
|
||||
ConvertAudioSamples<InputType,OutputType>(mStorage.get() + ReadIndex(),
|
||||
aOutputBuffer,
|
||||
samplesNeeded);
|
||||
} else {
|
||||
uint32_t firstPartLength = mLength - ReadIndex();
|
||||
uint32_t secondPartLength = samplesNeeded - firstPartLength;
|
||||
ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(),
|
||||
aOutputBuffer,
|
||||
firstPartLength);
|
||||
ConvertAudioSamples<InputType, OutputType>(mStorage.get(),
|
||||
aOutputBuffer + firstPartLength,
|
||||
secondPartLength);
|
||||
}
|
||||
mReadIndex += samplesNeeded;
|
||||
}
|
||||
|
||||
uint32_t PacketsAvailable() const {
|
||||
return AvailableSamples() / mChannels / mPacketSize;
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
return mWriteIndex == mReadIndex;
|
||||
}
|
||||
|
||||
bool Full() const {
|
||||
return mWriteIndex - mReadIndex == mLength;
|
||||
}
|
||||
|
||||
uint32_t PacketSize() const {
|
||||
return mPacketSize;
|
||||
}
|
||||
|
||||
uint32_t Channels() const {
|
||||
return mChannels;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t ReadIndex() const {
|
||||
return mReadIndex % mLength;
|
||||
}
|
||||
|
||||
uint32_t WriteIndex() const {
|
||||
return mWriteIndex % mLength;
|
||||
}
|
||||
|
||||
uint32_t AvailableSamples() const {
|
||||
return mWriteIndex - mReadIndex;
|
||||
}
|
||||
|
||||
uint32_t EmptySlots() const {
|
||||
return mLength - AvailableSamples();
|
||||
}
|
||||
|
||||
// Size of one packet of audio, in frames
|
||||
uint32_t mPacketSize;
|
||||
// Number of channels of the stream flowing through this packetizer
|
||||
uint32_t mChannels;
|
||||
// Two virtual index into the buffer: the read position and the write
|
||||
// position.
|
||||
uint64_t mReadIndex;
|
||||
uint64_t mWriteIndex;
|
||||
// Storage for the samples
|
||||
nsAutoPtr<InputType> mStorage;
|
||||
// Length of the buffer, in samples
|
||||
uint32_t mLength;
|
||||
};
|
||||
|
||||
} // mozilla
|
||||
|
||||
#endif // AudioPacketizer_h_
|
|
@ -96,6 +96,51 @@ FloatToAudioSample<int16_t>(float aValue)
|
|||
return int16_t(clamped);
|
||||
}
|
||||
|
||||
template <typename T> T IntegerToAudioSample(int16_t aValue);
|
||||
|
||||
template <> inline float
|
||||
IntegerToAudioSample<float>(int16_t aValue)
|
||||
{
|
||||
return aValue / 32768.0f;
|
||||
}
|
||||
template <> inline int16_t
|
||||
IntegerToAudioSample<int16_t>(int16_t aValue)
|
||||
{
|
||||
return aValue;
|
||||
}
|
||||
|
||||
template<typename SrcT, typename DstT>
|
||||
inline void
|
||||
ConvertAudioSample(SrcT aIn, DstT& aOut);
|
||||
|
||||
template<>
|
||||
inline void
|
||||
ConvertAudioSample(int16_t aIn, int16_t & aOut)
|
||||
{
|
||||
aOut = aIn;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void
|
||||
ConvertAudioSample(int16_t aIn, float& aOut)
|
||||
{
|
||||
aOut = AudioSampleToFloat(aIn);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void
|
||||
ConvertAudioSample(float aIn, float& aOut)
|
||||
{
|
||||
aOut = aIn;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void
|
||||
ConvertAudioSample(float aIn, int16_t& aOut)
|
||||
{
|
||||
aOut = FloatToAudioSample<int16_t>(aIn);
|
||||
}
|
||||
|
||||
// Sample buffer conversion
|
||||
|
||||
template <typename From, typename To> inline void
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "AudioSegment.h"
|
||||
|
||||
#include "AudioStream.h"
|
||||
#include "AudioMixer.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "Latency.h"
|
||||
|
@ -13,49 +12,18 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
template <class SrcT, class DestT>
|
||||
static void
|
||||
InterleaveAndConvertBuffer(const SrcT** aSourceChannels,
|
||||
int32_t aLength, float aVolume,
|
||||
int32_t aChannels,
|
||||
DestT* aOutput)
|
||||
const uint8_t SilentChannel::gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*SilentChannel::AUDIO_PROCESSING_FRAMES] = {0};
|
||||
|
||||
template<>
|
||||
const float* SilentChannel::ZeroChannel<float>()
|
||||
{
|
||||
DestT* output = aOutput;
|
||||
for (int32_t i = 0; i < aLength; ++i) {
|
||||
for (int32_t channel = 0; channel < aChannels; ++channel) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
++output;
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<const float*>(SilentChannel::gZeroChannel);
|
||||
}
|
||||
|
||||
void
|
||||
InterleaveAndConvertBuffer(const void** aSourceChannels,
|
||||
AudioSampleFormat aSourceFormat,
|
||||
int32_t aLength, float aVolume,
|
||||
int32_t aChannels,
|
||||
AudioDataValue* aOutput)
|
||||
template<>
|
||||
const int16_t* SilentChannel::ZeroChannel<int16_t>()
|
||||
{
|
||||
switch (aSourceFormat) {
|
||||
case AUDIO_FORMAT_FLOAT32:
|
||||
InterleaveAndConvertBuffer(reinterpret_cast<const float**>(aSourceChannels),
|
||||
aLength,
|
||||
aVolume,
|
||||
aChannels,
|
||||
aOutput);
|
||||
break;
|
||||
case AUDIO_FORMAT_S16:
|
||||
InterleaveAndConvertBuffer(reinterpret_cast<const int16_t**>(aSourceChannels),
|
||||
aLength,
|
||||
aVolume,
|
||||
aChannels,
|
||||
aOutput);
|
||||
break;
|
||||
case AUDIO_FORMAT_SILENCE:
|
||||
// nothing to do here.
|
||||
break;
|
||||
}
|
||||
return reinterpret_cast<const int16_t*>(SilentChannel::gZeroChannel);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -66,54 +34,6 @@ AudioSegment::ApplyVolume(float aVolume)
|
|||
}
|
||||
}
|
||||
|
||||
static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
|
||||
static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0};
|
||||
|
||||
void
|
||||
DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
|
||||
AudioSampleFormat aSourceFormat, int32_t aDuration,
|
||||
float aVolume, uint32_t aOutputChannels,
|
||||
AudioDataValue* aOutput)
|
||||
{
|
||||
nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channelData;
|
||||
nsAutoTArray<float,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> downmixConversionBuffer;
|
||||
nsAutoTArray<float,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> downmixOutputBuffer;
|
||||
|
||||
channelData.SetLength(aChannelData.Length());
|
||||
if (aSourceFormat != AUDIO_FORMAT_FLOAT32) {
|
||||
NS_ASSERTION(aSourceFormat == AUDIO_FORMAT_S16, "unknown format");
|
||||
downmixConversionBuffer.SetLength(aDuration*aChannelData.Length());
|
||||
for (uint32_t i = 0; i < aChannelData.Length(); ++i) {
|
||||
float* conversionBuf = downmixConversionBuffer.Elements() + (i*aDuration);
|
||||
const int16_t* sourceBuf = static_cast<const int16_t*>(aChannelData[i]);
|
||||
for (uint32_t j = 0; j < (uint32_t)aDuration; ++j) {
|
||||
conversionBuf[j] = AudioSampleToFloat(sourceBuf[j]);
|
||||
}
|
||||
channelData[i] = conversionBuf;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < aChannelData.Length(); ++i) {
|
||||
channelData[i] = aChannelData[i];
|
||||
}
|
||||
}
|
||||
|
||||
downmixOutputBuffer.SetLength(aDuration*aOutputChannels);
|
||||
nsAutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannelBuffers;
|
||||
nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> outputChannelData;
|
||||
outputChannelBuffers.SetLength(aOutputChannels);
|
||||
outputChannelData.SetLength(aOutputChannels);
|
||||
for (uint32_t i = 0; i < (uint32_t)aOutputChannels; ++i) {
|
||||
outputChannelData[i] = outputChannelBuffers[i] =
|
||||
downmixOutputBuffer.Elements() + aDuration*i;
|
||||
}
|
||||
if (channelData.Length() > aOutputChannels) {
|
||||
AudioChannelsDownMix(channelData, outputChannelBuffers.Elements(),
|
||||
aOutputChannels, aDuration);
|
||||
}
|
||||
InterleaveAndConvertBuffer(outputChannelData.Elements(), AUDIO_FORMAT_FLOAT32,
|
||||
aDuration, aVolume, aOutputChannels, aOutput);
|
||||
}
|
||||
|
||||
void AudioSegment::ResampleChunks(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate)
|
||||
{
|
||||
if (mChunks.IsEmpty()) {
|
||||
|
@ -165,9 +85,9 @@ void
|
|||
AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
|
||||
uint32_t aSampleRate)
|
||||
{
|
||||
nsAutoTArray<AudioDataValue, AUDIO_PROCESSING_FRAMES* GUESS_AUDIO_CHANNELS>
|
||||
nsAutoTArray<AudioDataValue, SilentChannel::AUDIO_PROCESSING_FRAMES* GUESS_AUDIO_CHANNELS>
|
||||
buf;
|
||||
nsAutoTArray<const void*, GUESS_AUDIO_CHANNELS> channelData;
|
||||
nsAutoTArray<const AudioDataValue*, GUESS_AUDIO_CHANNELS> channelData;
|
||||
uint32_t offsetSamples = 0;
|
||||
uint32_t duration = GetDuration();
|
||||
|
||||
|
@ -197,11 +117,11 @@ AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
|
|||
// desired input and output channels.
|
||||
channelData.SetLength(c.mChannelData.Length());
|
||||
for (uint32_t i = 0; i < channelData.Length(); ++i) {
|
||||
channelData[i] = c.mChannelData[i];
|
||||
channelData[i] = static_cast<const AudioDataValue*>(c.mChannelData[i]);
|
||||
}
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, gZeroChannel);
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<AudioDataValue>());
|
||||
for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
|
||||
AudioDataValue* ptr =
|
||||
PointerForOffsetInChannel(buf.Elements(), outBufferLength,
|
||||
|
@ -246,9 +166,8 @@ AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
|
|||
void
|
||||
AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels, uint32_t aSampleRate)
|
||||
{
|
||||
nsAutoTArray<AudioDataValue,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
|
||||
nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channelData;
|
||||
// Offset in the buffer that will end up sent to the AudioStream, in samples.
|
||||
nsAutoTArray<AudioDataValue,SilentChannel::AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
|
||||
// Offset in the buffer that will be written to the mixer, in samples.
|
||||
uint32_t offset = 0;
|
||||
|
||||
if (GetDuration() <= 0) {
|
||||
|
@ -262,39 +181,23 @@ AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels
|
|||
|
||||
for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
|
||||
AudioChunk& c = *ci;
|
||||
uint32_t frames = c.mDuration;
|
||||
|
||||
// If we have written data in the past, or we have real (non-silent) data
|
||||
// to write, we can proceed. Otherwise, it means we just started the
|
||||
// AudioStream, and we don't have real data to write to it (just silence).
|
||||
// To avoid overbuffering in the AudioStream, we simply drop the silence,
|
||||
// here. The stream will underrun and output silence anyways.
|
||||
if (c.mBuffer && c.mBufferFormat != AUDIO_FORMAT_SILENCE) {
|
||||
channelData.SetLength(c.mChannelData.Length());
|
||||
for (uint32_t i = 0; i < channelData.Length(); ++i) {
|
||||
channelData[i] = c.mChannelData[i];
|
||||
}
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix. Note that this might actually make channelData have more
|
||||
// than aOutputChannels temporarily.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, gZeroChannel);
|
||||
}
|
||||
if (channelData.Length() > aOutputChannels) {
|
||||
// Down-mix.
|
||||
DownmixAndInterleave(channelData, c.mBufferFormat, frames,
|
||||
c.mVolume, aOutputChannels, buf.Elements() + offset);
|
||||
} else {
|
||||
InterleaveAndConvertBuffer(channelData.Elements(), c.mBufferFormat,
|
||||
frames, c.mVolume,
|
||||
aOutputChannels,
|
||||
buf.Elements() + offset);
|
||||
}
|
||||
} else {
|
||||
// Assumes that a bit pattern of zeroes == 0.0f
|
||||
memset(buf.Elements() + offset, 0, aOutputChannels * frames * sizeof(AudioDataValue));
|
||||
switch (c.mBufferFormat) {
|
||||
case AUDIO_FORMAT_S16:
|
||||
WriteChunk<int16_t>(c, aOutputChannels, buf.Elements() + offset);
|
||||
break;
|
||||
case AUDIO_FORMAT_FLOAT32:
|
||||
WriteChunk<float>(c, aOutputChannels, buf.Elements() + offset);
|
||||
break;
|
||||
case AUDIO_FORMAT_SILENCE:
|
||||
// The mixer is expecting interleaved data, so this is ok.
|
||||
PodZero(buf.Elements() + offset, c.mDuration * aOutputChannels);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false, "Not handled");
|
||||
}
|
||||
|
||||
offset += frames * aOutputChannels;
|
||||
offset += c.mDuration * aOutputChannels;
|
||||
|
||||
#if !defined(MOZILLA_XPCOMRT_API)
|
||||
if (!c.mTimeStamp.IsNull()) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "MediaSegment.h"
|
||||
#include "AudioSampleFormat.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "SharedBuffer.h"
|
||||
#include "WebAudioUtils.h"
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
|
@ -56,21 +57,82 @@ const int GUESS_AUDIO_CHANNELS = 2;
|
|||
const uint32_t WEBAUDIO_BLOCK_SIZE_BITS = 7;
|
||||
const uint32_t WEBAUDIO_BLOCK_SIZE = 1 << WEBAUDIO_BLOCK_SIZE_BITS;
|
||||
|
||||
void InterleaveAndConvertBuffer(const void** aSourceChannels,
|
||||
AudioSampleFormat aSourceFormat,
|
||||
int32_t aLength, float aVolume,
|
||||
int32_t aChannels,
|
||||
AudioDataValue* aOutput);
|
||||
template <typename SrcT, typename DestT>
|
||||
static void
|
||||
InterleaveAndConvertBuffer(const SrcT* const* aSourceChannels,
|
||||
uint32_t aLength, float aVolume,
|
||||
uint32_t aChannels,
|
||||
DestT* aOutput)
|
||||
{
|
||||
DestT* output = aOutput;
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
for (size_t channel = 0; channel < aChannels; ++channel) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
++output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SrcT, typename DestT>
|
||||
static void
|
||||
DeinterleaveAndConvertBuffer(const SrcT* aSourceBuffer,
|
||||
uint32_t aFrames, uint32_t aChannels,
|
||||
DestT** aOutput)
|
||||
{
|
||||
for (size_t i = 0; i < aChannels; i++) {
|
||||
size_t interleavedIndex = i;
|
||||
for (size_t j = 0; j < aFrames; j++) {
|
||||
ConvertAudioSample(aSourceBuffer[interleavedIndex],
|
||||
aOutput[i][j]);
|
||||
interleavedIndex += aChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SilentChannel
|
||||
{
|
||||
public:
|
||||
static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
|
||||
static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES];
|
||||
// We take advantage of the fact that zero in float and zero in int have the
|
||||
// same all-zeros bit layout.
|
||||
template<typename T>
|
||||
static const T* ZeroChannel();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Given an array of input channels (aChannelData), downmix to aOutputChannels,
|
||||
* interleave the channel data. A total of aOutputChannels*aDuration
|
||||
* interleaved samples will be copied to a channel buffer in aOutput.
|
||||
*/
|
||||
void DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
|
||||
AudioSampleFormat aSourceFormat, int32_t aDuration,
|
||||
float aVolume, uint32_t aOutputChannels,
|
||||
AudioDataValue* aOutput);
|
||||
template <typename SrcT, typename DestT>
|
||||
void
|
||||
DownmixAndInterleave(const nsTArray<const SrcT*>& aChannelData,
|
||||
int32_t aDuration, float aVolume, uint32_t aOutputChannels,
|
||||
DestT* aOutput)
|
||||
{
|
||||
|
||||
if (aChannelData.Length() == aOutputChannels) {
|
||||
InterleaveAndConvertBuffer(aChannelData.Elements(),
|
||||
aDuration, aVolume, aOutputChannels, aOutput);
|
||||
} else {
|
||||
nsAutoTArray<SrcT*,GUESS_AUDIO_CHANNELS> outputChannelData;
|
||||
nsAutoTArray<SrcT, SilentChannel::AUDIO_PROCESSING_FRAMES * GUESS_AUDIO_CHANNELS> outputBuffers;
|
||||
outputChannelData.SetLength(aOutputChannels);
|
||||
outputBuffers.SetLength(aDuration * aOutputChannels);
|
||||
for (uint32_t i = 0; i < aOutputChannels; i++) {
|
||||
outputChannelData[i] = outputBuffers.Elements() + aDuration * i;
|
||||
}
|
||||
AudioChannelsDownMix(aChannelData,
|
||||
outputChannelData.Elements(),
|
||||
aOutputChannels,
|
||||
aDuration);
|
||||
InterleaveAndConvertBuffer(outputChannelData.Elements(),
|
||||
aDuration, aVolume, aOutputChannels, aOutput);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An AudioChunk represents a multi-channel buffer of audio samples.
|
||||
|
@ -190,6 +252,13 @@ struct AudioChunk {
|
|||
return amount;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const nsTArray<const T*>& ChannelData()
|
||||
{
|
||||
MOZ_ASSERT(AudioSampleTypeToFormat<T>::Format == mBufferFormat);
|
||||
return *reinterpret_cast<nsTArray<const T*>*>(&mChannelData);
|
||||
}
|
||||
|
||||
StreamTime mDuration; // in frames within the buffer
|
||||
nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes
|
||||
nsTArray<const void*> mChannelData; // one pointer per channel; empty if and only if mBuffer is null
|
||||
|
@ -356,6 +425,34 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<typename SrcT>
|
||||
void WriteChunk(AudioChunk& aChunk,
|
||||
uint32_t aOutputChannels,
|
||||
AudioDataValue* aOutputBuffer)
|
||||
{
|
||||
nsAutoTArray<const SrcT*,GUESS_AUDIO_CHANNELS> channelData;
|
||||
|
||||
channelData = aChunk.ChannelData<SrcT>();
|
||||
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix. Note that this might actually make channelData have more
|
||||
// than aOutputChannels temporarily.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<SrcT>());
|
||||
}
|
||||
if (channelData.Length() > aOutputChannels) {
|
||||
// Down-mix.
|
||||
DownmixAndInterleave(channelData, aChunk.mDuration,
|
||||
aChunk.mVolume, aOutputChannels, aOutputBuffer);
|
||||
} else {
|
||||
InterleaveAndConvertBuffer(channelData.Elements(),
|
||||
aChunk.mDuration, aChunk.mVolume,
|
||||
aOutputChannels,
|
||||
aOutputBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_AUDIOSEGMENT_H_ */
|
||||
|
|
|
@ -359,6 +359,7 @@ DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
|
|||
, mShuttingDown(false)
|
||||
, mPlaying(false)
|
||||
, mVolume(1.0)
|
||||
, mSameOrigin(false)
|
||||
, mAudioQueue(aAudioQueue)
|
||||
, mVideoQueue(aVideoQueue)
|
||||
{
|
||||
|
|
|
@ -2483,24 +2483,19 @@ SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSe
|
|||
AudioSegment* segment = static_cast<AudioSegment*>(aSegment);
|
||||
int channels = segment->ChannelCount();
|
||||
|
||||
// If this segment is just silence, we delay instanciating the resampler.
|
||||
if (channels) {
|
||||
if (aTrackData->mResampler) {
|
||||
MOZ_ASSERT(aTrackData->mResamplerChannelCount == segment->ChannelCount());
|
||||
} else {
|
||||
SpeexResamplerState* state = speex_resampler_init(channels,
|
||||
aTrackData->mInputRate,
|
||||
GraphImpl()->GraphRate(),
|
||||
SPEEX_RESAMPLER_QUALITY_MIN,
|
||||
nullptr);
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
aTrackData->mResampler.own(state);
|
||||
#ifdef DEBUG
|
||||
aTrackData->mResamplerChannelCount = channels;
|
||||
#endif
|
||||
// If this segment is just silence, we delay instanciating the resampler. We
|
||||
// also need to recreate the resampler if the channel count changes.
|
||||
if (channels && aTrackData->mResamplerChannelCount != channels) {
|
||||
SpeexResamplerState* state = speex_resampler_init(channels,
|
||||
aTrackData->mInputRate,
|
||||
GraphImpl()->GraphRate(),
|
||||
SPEEX_RESAMPLER_QUALITY_MIN,
|
||||
nullptr);
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
aTrackData->mResampler.own(state);
|
||||
aTrackData->mResamplerChannelCount = channels;
|
||||
}
|
||||
segment->ResampleChunks(aTrackData->mResampler, aTrackData->mInputRate, GraphImpl()->GraphRate());
|
||||
}
|
||||
|
|
|
@ -897,9 +897,7 @@ protected:
|
|||
// Resampler if the rate of the input track does not match the
|
||||
// MediaStreamGraph's.
|
||||
nsAutoRef<SpeexResamplerState> mResampler;
|
||||
#ifdef DEBUG
|
||||
int mResamplerChannelCount;
|
||||
#endif
|
||||
StreamTime mStart;
|
||||
// End-time of data already flushed to the track (excluding mData)
|
||||
StreamTime mEndOfFlushedData;
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 <stdint.h>
|
||||
#include <assert.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include "../AudioPacketizer.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
template<typename T>
|
||||
class AutoBuffer
|
||||
{
|
||||
public:
|
||||
explicit AutoBuffer(size_t aLength)
|
||||
{
|
||||
mStorage = new T[aLength];
|
||||
}
|
||||
~AutoBuffer() {
|
||||
delete [] mStorage;
|
||||
}
|
||||
T* Get() {
|
||||
return mStorage;
|
||||
}
|
||||
private:
|
||||
T* mStorage;
|
||||
};
|
||||
|
||||
int16_t Sequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < aSize; i++) {
|
||||
aBuffer[i] = aStart + i;
|
||||
}
|
||||
return aStart + i;
|
||||
}
|
||||
|
||||
void IsSequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0)
|
||||
{
|
||||
for (uint32_t i = 0; i < aSize; i++) {
|
||||
if (aBuffer[i] != static_cast<int64_t>(aStart + i)) {
|
||||
fprintf(stderr, "Buffer is not a sequence at offset %u\n", i);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
assert("Buffer is a sequence.");
|
||||
}
|
||||
|
||||
void Zero(int16_t* aBuffer, uint32_t aSize)
|
||||
{
|
||||
for (uint32_t i = 0; i < aSize; i++) {
|
||||
if (aBuffer[i] != 0) {
|
||||
fprintf(stderr, "Buffer is not null at offset %u\n", i);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double sine(uint32_t aPhase) {
|
||||
return sin(aPhase * 2 * M_PI * 440 / 44100);
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int16_t channels = 1; channels < 2; channels++) {
|
||||
// Test that the packetizer returns zero on underrun
|
||||
{
|
||||
AudioPacketizer<int16_t, int16_t> ap(441, channels);
|
||||
for (int16_t i = 0; i < 10; i++) {
|
||||
int16_t* out = ap.Output();
|
||||
Zero(out, 441);
|
||||
delete out;
|
||||
}
|
||||
}
|
||||
// Simple test, with input/output buffer size aligned on the packet size,
|
||||
// alternating Input and Output calls.
|
||||
{
|
||||
AudioPacketizer<int16_t, int16_t> ap(441, channels);
|
||||
int16_t seqEnd = 0;
|
||||
for (int16_t i = 0; i < 10; i++) {
|
||||
AutoBuffer<int16_t> b(441 * channels);
|
||||
int16_t prevEnd = seqEnd;
|
||||
seqEnd = Sequence(b.Get(), channels * 441, prevEnd);
|
||||
ap.Input(b.Get(), 441);
|
||||
int16_t* out = ap.Output();
|
||||
IsSequence(out, 441 * channels, prevEnd);
|
||||
delete out;
|
||||
}
|
||||
}
|
||||
// Simple test, with input/output buffer size aligned on the packet size,
|
||||
// alternating two Input and Output calls.
|
||||
{
|
||||
AudioPacketizer<int16_t, int16_t> ap(441, channels);
|
||||
int16_t seqEnd = 0;
|
||||
for (int16_t i = 0; i < 10; i++) {
|
||||
AutoBuffer<int16_t> b(441 * channels);
|
||||
AutoBuffer<int16_t> b1(441 * channels);
|
||||
int16_t prevEnd0 = seqEnd;
|
||||
seqEnd = Sequence(b.Get(), 441 * channels, prevEnd0);
|
||||
int16_t prevEnd1 = seqEnd;
|
||||
seqEnd = Sequence(b1.Get(), 441 * channels, seqEnd);
|
||||
ap.Input(b.Get(), 441);
|
||||
ap.Input(b1.Get(), 441);
|
||||
int16_t* out = ap.Output();
|
||||
int16_t* out2 = ap.Output();
|
||||
IsSequence(out, 441 * channels, prevEnd0);
|
||||
IsSequence(out2, 441 * channels, prevEnd1);
|
||||
delete out;
|
||||
delete out2;
|
||||
}
|
||||
}
|
||||
// Input/output buffer size not aligned on the packet size,
|
||||
// alternating two Input and Output calls.
|
||||
{
|
||||
AudioPacketizer<int16_t, int16_t> ap(441, channels);
|
||||
int16_t prevEnd = 0;
|
||||
int16_t prevSeq = 0;
|
||||
for (int16_t i = 0; i < 10; i++) {
|
||||
AutoBuffer<int16_t> b(480 * channels);
|
||||
AutoBuffer<int16_t> b1(480 * channels);
|
||||
prevSeq = Sequence(b.Get(), 480 * channels, prevSeq);
|
||||
prevSeq = Sequence(b1.Get(), 480 * channels, prevSeq);
|
||||
ap.Input(b.Get(), 480);
|
||||
ap.Input(b1.Get(), 480);
|
||||
int16_t* out = ap.Output();
|
||||
int16_t* out2 = ap.Output();
|
||||
IsSequence(out, 441 * channels, prevEnd);
|
||||
prevEnd += 441 * channels;
|
||||
IsSequence(out2, 441 * channels, prevEnd);
|
||||
prevEnd += 441 * channels;
|
||||
delete out;
|
||||
delete out2;
|
||||
}
|
||||
printf("Available: %d\n", ap.PacketsAvailable());
|
||||
}
|
||||
|
||||
// "Real-life" test case: streaming a sine wave through a packetizer, and
|
||||
// checking that we have the right output.
|
||||
// 128 is, for example, the size of a Web Audio API block, and 441 is the
|
||||
// size of a webrtc.org packet when the sample rate is 44100 (10ms)
|
||||
{
|
||||
AudioPacketizer<int16_t, int16_t> ap(441, channels);
|
||||
AutoBuffer<int16_t> b(128 * channels);
|
||||
uint32_t phase = 0;
|
||||
uint32_t outPhase = 0;
|
||||
for (int16_t i = 0; i < 1000; i++) {
|
||||
for (int32_t j = 0; j < 128; j++) {
|
||||
for (int32_t c = 0; c < channels; c++) {
|
||||
// int16_t sinewave at 440Hz/44100Hz sample rate
|
||||
b.Get()[j * channels + c] = (2 << 14) * sine(phase);
|
||||
}
|
||||
phase++;
|
||||
}
|
||||
ap.Input(b.Get(), 128);
|
||||
while (ap.PacketsAvailable()) {
|
||||
int16_t* packet = ap.Output();
|
||||
for (uint32_t k = 0; k < ap.PacketSize(); k++) {
|
||||
for (int32_t c = 0; c < channels; c++) {
|
||||
assert(packet[k * channels + c] ==
|
||||
static_cast<int16_t>(((2 << 14) * sine(outPhase))));
|
||||
}
|
||||
outPhase++;
|
||||
}
|
||||
delete [] packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("OK\n");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "AudioSegment.h"
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla {
|
||||
uint32_t
|
||||
GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2)
|
||||
{
|
||||
return std::max(aChannels1, aChannels2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to give us the maximum and minimum value that don't clip,
|
||||
* for a given sample format (integer or floating-point). */
|
||||
template<typename T>
|
||||
T GetLowValue();
|
||||
|
||||
template<typename T>
|
||||
T GetHighValue();
|
||||
|
||||
template<typename T>
|
||||
T GetSilentValue();
|
||||
|
||||
template<>
|
||||
float GetLowValue<float>() {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
template<>
|
||||
int16_t GetLowValue<short>() {
|
||||
return -INT16_MAX;
|
||||
}
|
||||
|
||||
template<>
|
||||
float GetHighValue<float>() {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
template<>
|
||||
int16_t GetHighValue<short>() {
|
||||
return INT16_MAX;
|
||||
}
|
||||
|
||||
template<>
|
||||
float GetSilentValue() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
template<>
|
||||
int16_t GetSilentValue() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Get an array of planar audio buffers that has the inverse of the index of the
|
||||
// channel (1-indexed) as samples.
|
||||
template<typename T>
|
||||
const T* const* GetPlanarChannelArray(size_t aChannels, size_t aSize)
|
||||
{
|
||||
T** channels = new T*[aChannels];
|
||||
for (size_t c = 0; c < aChannels; c++) {
|
||||
channels[c] = new T[aSize];
|
||||
for (size_t i = 0; i < aSize; i++) {
|
||||
channels[c][i] = FloatToAudioSample<T>(1. / (c + 1));
|
||||
}
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DeletePlanarChannelsArray(const T* const* aArrays, size_t aChannels)
|
||||
{
|
||||
for (size_t channel = 0; channel < aChannels; channel++) {
|
||||
delete [] aArrays[channel];
|
||||
}
|
||||
delete [] aArrays;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T** GetPlanarArray(size_t aChannels, size_t aSize)
|
||||
{
|
||||
T** channels = new T*[aChannels];
|
||||
for (size_t c = 0; c < aChannels; c++) {
|
||||
channels[c] = new T[aSize];
|
||||
for (size_t i = 0; i < aSize; i++) {
|
||||
channels[c][i] = 0.0f;
|
||||
}
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DeletePlanarArray(T** aArrays, size_t aChannels)
|
||||
{
|
||||
for (size_t channel = 0; channel < aChannels; channel++) {
|
||||
delete [] aArrays[channel];
|
||||
}
|
||||
delete [] aArrays;
|
||||
}
|
||||
|
||||
// Get an array of audio samples that have the inverse of the index of the
|
||||
// channel (1-indexed) as samples.
|
||||
template<typename T>
|
||||
const T* GetInterleavedChannelArray(size_t aChannels, size_t aSize)
|
||||
{
|
||||
size_t sampleCount = aChannels * aSize;
|
||||
T* samples = new T[sampleCount];
|
||||
for (size_t i = 0; i < sampleCount; i++) {
|
||||
uint32_t channel = (i % aChannels) + 1;
|
||||
samples[i] = FloatToAudioSample<T>(1. / channel);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DeleteInterleavedChannelArray(const T* aArray)
|
||||
{
|
||||
delete [] aArray;
|
||||
}
|
||||
|
||||
bool FuzzyEqual(float aLhs, float aRhs) {
|
||||
return std::abs(aLhs - aRhs) < 0.01;
|
||||
}
|
||||
|
||||
template<typename SrcT, typename DstT>
|
||||
void TestInterleaveAndConvert()
|
||||
{
|
||||
size_t arraySize = 1024;
|
||||
size_t maxChannels = 8; // 7.1
|
||||
for (uint32_t channels = 1; channels < maxChannels; channels++) {
|
||||
const SrcT* const* src = GetPlanarChannelArray<SrcT>(channels, arraySize);
|
||||
DstT* dst = new DstT[channels * arraySize];
|
||||
|
||||
InterleaveAndConvertBuffer(src, arraySize, 1.0, channels, dst);
|
||||
|
||||
uint32_t channelIndex = 0;
|
||||
for (size_t i = 0; i < arraySize * channels; i++) {
|
||||
assert(FuzzyEqual(dst[i],
|
||||
FloatToAudioSample<DstT>(1. / (channelIndex + 1))));
|
||||
channelIndex++;
|
||||
channelIndex %= channels;
|
||||
}
|
||||
|
||||
DeletePlanarChannelsArray(src, channels);
|
||||
delete [] dst;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename SrcT, typename DstT>
|
||||
void TestDeinterleaveAndConvert()
|
||||
{
|
||||
size_t arraySize = 1024;
|
||||
size_t maxChannels = 8; // 7.1
|
||||
for (uint32_t channels = 1; channels < maxChannels; channels++) {
|
||||
const SrcT* src = GetInterleavedChannelArray<SrcT>(channels, arraySize);
|
||||
DstT** dst = GetPlanarArray<DstT>(channels, arraySize);
|
||||
|
||||
DeinterleaveAndConvertBuffer(src, arraySize, channels, dst);
|
||||
|
||||
for (size_t channel = 0; channel < channels; channel++) {
|
||||
for (size_t i = 0; i < arraySize; i++) {
|
||||
assert(FuzzyEqual(dst[channel][i],
|
||||
FloatToAudioSample<DstT>(1. / (channel + 1))));
|
||||
}
|
||||
}
|
||||
|
||||
DeleteInterleavedChannelArray(src);
|
||||
DeletePlanarArray(dst, channels);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t gSilence[4096] = {0};
|
||||
|
||||
template<typename T>
|
||||
T* SilentChannel()
|
||||
{
|
||||
return reinterpret_cast<T*>(gSilence);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void TestUpmixStereo()
|
||||
{
|
||||
size_t arraySize = 1024;
|
||||
nsTArray<T*> channels;
|
||||
nsTArray<const T*> channelsptr;
|
||||
|
||||
channels.SetLength(1);
|
||||
channelsptr.SetLength(1);
|
||||
|
||||
channels[0] = new T[arraySize];
|
||||
|
||||
for (size_t i = 0; i < arraySize; i++) {
|
||||
channels[0][i] = GetHighValue<T>();
|
||||
}
|
||||
channelsptr[0] = channels[0];
|
||||
|
||||
AudioChannelsUpMix(&channelsptr, 2, ::SilentChannel<T>());
|
||||
|
||||
for (size_t channel = 0; channel < 2; channel++) {
|
||||
for (size_t i = 0; i < arraySize; i++) {
|
||||
if (channelsptr[channel][i] != GetHighValue<T>()) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(true);
|
||||
delete channels[0];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void TestDownmixStereo()
|
||||
{
|
||||
const size_t arraySize = 1024;
|
||||
nsTArray<const T*> inputptr;
|
||||
nsTArray<T*> input;
|
||||
T** output;
|
||||
|
||||
output = new T*[1];
|
||||
output[0] = new T[arraySize];
|
||||
|
||||
input.SetLength(2);
|
||||
inputptr.SetLength(2);
|
||||
|
||||
for (size_t channel = 0; channel < input.Length(); channel++) {
|
||||
input[channel] = new T[arraySize];
|
||||
for (size_t i = 0; i < arraySize; i++) {
|
||||
input[channel][i] = channel == 0 ? GetLowValue<T>() : GetHighValue<T>();
|
||||
}
|
||||
inputptr[channel] = input[channel];
|
||||
}
|
||||
|
||||
AudioChannelsDownMix(inputptr, output, 1, arraySize);
|
||||
|
||||
for (size_t i = 0; i < arraySize; i++) {
|
||||
if (output[0][i] != GetSilentValue<T>()) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
assert(true);
|
||||
|
||||
delete output[0];
|
||||
delete output;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
TestInterleaveAndConvert<float, float>();
|
||||
TestInterleaveAndConvert<float, int16_t>();
|
||||
TestInterleaveAndConvert<int16_t, float>();
|
||||
TestInterleaveAndConvert<int16_t, int16_t>();
|
||||
TestDeinterleaveAndConvert<float, float>();
|
||||
TestDeinterleaveAndConvert<float, int16_t>();
|
||||
TestDeinterleaveAndConvert<int16_t, float>();
|
||||
TestDeinterleaveAndConvert<int16_t, int16_t>();
|
||||
TestUpmixStereo<float>();
|
||||
TestUpmixStereo<int16_t>();
|
||||
TestDownmixStereo<float>();
|
||||
TestDownmixStereo<int16_t>();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
GeckoCppUnitTests([
|
||||
'TestAudioBuffers',
|
||||
'TestAudioMixer'
|
||||
'TestAudioMixer',
|
||||
'TestAudioPacketizer',
|
||||
'TestAudioSegment'
|
||||
])
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
|
|
@ -123,9 +123,6 @@ AudioTrackEncoder::AppendAudioSegment(const AudioSegment& aSegment)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
|
||||
static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0};
|
||||
|
||||
/*static*/
|
||||
void
|
||||
AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
|
||||
|
@ -133,19 +130,29 @@ AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
|
|||
uint32_t aOutputChannels,
|
||||
AudioDataValue* aOutput)
|
||||
{
|
||||
if (aChunk.mChannelData.Length() < aOutputChannels) {
|
||||
// Up-mix. This might make the mChannelData have more than aChannels.
|
||||
AudioChannelsUpMix(&aChunk.mChannelData, aOutputChannels, gZeroChannel);
|
||||
}
|
||||
|
||||
if (aChunk.mChannelData.Length() > aOutputChannels) {
|
||||
DownmixAndInterleave(aChunk.mChannelData, aChunk.mBufferFormat, aDuration,
|
||||
aChunk.mVolume, aOutputChannels, aOutput);
|
||||
} else {
|
||||
InterleaveAndConvertBuffer(aChunk.mChannelData.Elements(),
|
||||
aChunk.mBufferFormat, aDuration, aChunk.mVolume,
|
||||
aOutputChannels, aOutput);
|
||||
}
|
||||
switch(aChunk.mBufferFormat) {
|
||||
case AUDIO_FORMAT_S16: {
|
||||
nsAutoTArray<const int16_t*, 2> array;
|
||||
array.SetLength(aOutputChannels);
|
||||
for (uint32_t i = 0; i < array.Length(); i++) {
|
||||
array[i] = static_cast<const int16_t*>(aChunk.mChannelData[i]);
|
||||
}
|
||||
InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
|
||||
break;
|
||||
}
|
||||
case AUDIO_FORMAT_FLOAT32: {
|
||||
nsAutoTArray<const float*, 2> array;
|
||||
array.SetLength(aOutputChannels);
|
||||
for (uint32_t i = 0; i < array.Length(); i++) {
|
||||
array[i] = static_cast<const float*>(aChunk.mChannelData[i]);
|
||||
}
|
||||
InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
|
||||
break;
|
||||
}
|
||||
case AUDIO_FORMAT_SILENCE: {
|
||||
MOZ_ASSERT(false, "To implement.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*static*/
|
||||
|
|
|
@ -148,6 +148,28 @@ public:
|
|||
uint32_t aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia) override;
|
||||
|
||||
template<typename T>
|
||||
static
|
||||
void InterleaveTrackData(nsTArray<const T*>& aInput,
|
||||
int32_t aDuration,
|
||||
uint32_t aOutputChannels,
|
||||
AudioDataValue* aOutput,
|
||||
float aVolume)
|
||||
{
|
||||
if (aInput.Length() < aOutputChannels) {
|
||||
// Up-mix. This might make the mChannelData have more than aChannels.
|
||||
AudioChannelsUpMix(&aInput, aOutputChannels, SilentChannel::ZeroChannel<T>());
|
||||
}
|
||||
|
||||
if (aInput.Length() > aOutputChannels) {
|
||||
DownmixAndInterleave(aInput, aDuration,
|
||||
aVolume, aOutputChannels, aOutput);
|
||||
} else {
|
||||
InterleaveAndConvertBuffer(aInput.Elements(), aDuration, aVolume,
|
||||
aOutputChannels, aOutput);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interleaves the track data and stores the result into aOutput. Might need
|
||||
* to up-mix or down-mix the channel data if the channels number of this chunk
|
||||
|
|
|
@ -101,6 +101,7 @@ EXPORTS += [
|
|||
'AudioChannelFormat.h',
|
||||
'AudioCompactor.h',
|
||||
'AudioMixer.h',
|
||||
'AudioPacketizer.h',
|
||||
'AudioSampleFormat.h',
|
||||
'AudioSegment.h',
|
||||
'AudioStream.h',
|
||||
|
|
|
@ -42,45 +42,33 @@ AudioNodeExternalInputStream::Create(MediaStreamGraph* aGraph,
|
|||
* aBlock must have been allocated with AllocateInputBlock and have a channel
|
||||
* count that's a superset of the channels in aInput.
|
||||
*/
|
||||
template <typename T>
|
||||
static void
|
||||
CopyChunkToBlock(const AudioChunk& aInput, AudioChunk *aBlock,
|
||||
CopyChunkToBlock(AudioChunk& aInput, AudioChunk *aBlock,
|
||||
uint32_t aOffsetInBlock)
|
||||
{
|
||||
uint32_t blockChannels = aBlock->ChannelCount();
|
||||
nsAutoTArray<const void*,2> channels;
|
||||
nsAutoTArray<const T*,2> channels;
|
||||
if (aInput.IsNull()) {
|
||||
channels.SetLength(blockChannels);
|
||||
PodZero(channels.Elements(), blockChannels);
|
||||
} else {
|
||||
channels.SetLength(aInput.ChannelCount());
|
||||
PodCopy(channels.Elements(), aInput.mChannelData.Elements(), channels.Length());
|
||||
const nsTArray<const T*>& inputChannels = aInput.ChannelData<T>();
|
||||
channels.SetLength(inputChannels.Length());
|
||||
PodCopy(channels.Elements(), inputChannels.Elements(), channels.Length());
|
||||
if (channels.Length() != blockChannels) {
|
||||
// We only need to upmix here because aBlock's channel count has been
|
||||
// chosen to be a superset of the channel count of every chunk.
|
||||
AudioChannelsUpMix(&channels, blockChannels, nullptr);
|
||||
AudioChannelsUpMix(&channels, blockChannels, static_cast<T*>(nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t duration = aInput.GetDuration();
|
||||
for (uint32_t c = 0; c < blockChannels; ++c) {
|
||||
float* outputData = aBlock->ChannelFloatsForWrite(c) + aOffsetInBlock;
|
||||
if (channels[c]) {
|
||||
switch (aInput.mBufferFormat) {
|
||||
case AUDIO_FORMAT_FLOAT32:
|
||||
ConvertAudioSamplesWithScale(
|
||||
static_cast<const float*>(channels[c]), outputData, duration,
|
||||
aInput.mVolume);
|
||||
break;
|
||||
case AUDIO_FORMAT_S16:
|
||||
ConvertAudioSamplesWithScale(
|
||||
static_cast<const int16_t*>(channels[c]), outputData, duration,
|
||||
aInput.mVolume);
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Unhandled format");
|
||||
}
|
||||
ConvertAudioSamplesWithScale(channels[c], outputData, aInput.GetDuration(), aInput.mVolume);
|
||||
} else {
|
||||
PodZero(outputData, duration);
|
||||
PodZero(outputData, aInput.GetDuration());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +99,18 @@ static void ConvertSegmentToAudioBlock(AudioSegment* aSegment,
|
|||
|
||||
uint32_t duration = 0;
|
||||
for (AudioSegment::ChunkIterator ci(*aSegment); !ci.IsEnded(); ci.Next()) {
|
||||
CopyChunkToBlock(*ci, aBlock, duration);
|
||||
switch (ci->mBufferFormat) {
|
||||
case AUDIO_FORMAT_S16: {
|
||||
CopyChunkToBlock<int16_t>(*ci, aBlock, duration);
|
||||
break;
|
||||
}
|
||||
case AUDIO_FORMAT_FLOAT32: {
|
||||
CopyChunkToBlock<float>(*ci, aBlock, duration);
|
||||
break;
|
||||
}
|
||||
case AUDIO_FORMAT_SILENCE:
|
||||
break;
|
||||
}
|
||||
duration += ci->GetDuration();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -430,7 +430,7 @@ AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aC
|
|||
AudioChunk* aBlock,
|
||||
nsTArray<float>* aDownmixBuffer)
|
||||
{
|
||||
nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channels;
|
||||
nsAutoTArray<const float*,GUESS_AUDIO_CHANNELS> channels;
|
||||
UpMixDownMixChunk(&aChunk, aBlock->mChannelData.Length(), channels, *aDownmixBuffer);
|
||||
|
||||
for (uint32_t c = 0; c < channels.Length(); ++c) {
|
||||
|
@ -453,15 +453,17 @@ AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aC
|
|||
void
|
||||
AudioNodeStream::UpMixDownMixChunk(const AudioChunk* aChunk,
|
||||
uint32_t aOutputChannelCount,
|
||||
nsTArray<const void*>& aOutputChannels,
|
||||
nsTArray<const float*>& aOutputChannels,
|
||||
nsTArray<float>& aDownmixBuffer)
|
||||
{
|
||||
static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {0.f};
|
||||
|
||||
aOutputChannels.AppendElements(aChunk->mChannelData);
|
||||
for (uint32_t i = 0; i < aChunk->mChannelData.Length(); i++) {
|
||||
aOutputChannels.AppendElement(static_cast<const float*>(aChunk->mChannelData[i]));
|
||||
}
|
||||
if (aOutputChannels.Length() < aOutputChannelCount) {
|
||||
if (mChannelInterpretation == ChannelInterpretation::Speakers) {
|
||||
AudioChannelsUpMix(&aOutputChannels, aOutputChannelCount, nullptr);
|
||||
AudioChannelsUpMix(&aOutputChannels, aOutputChannelCount, SilentChannel::ZeroChannel<float>());
|
||||
NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
|
||||
"We called GetAudioChannelsSuperset to avoid this");
|
||||
} else {
|
||||
|
|
|
@ -183,7 +183,7 @@ protected:
|
|||
AudioChunk* aBlock,
|
||||
nsTArray<float>* aDownmixBuffer);
|
||||
void UpMixDownMixChunk(const AudioChunk* aChunk, uint32_t aOutputChannelCount,
|
||||
nsTArray<const void*>& aOutputChannels,
|
||||
nsTArray<const float*>& aOutputChannels,
|
||||
nsTArray<float>& aDownmixBuffer);
|
||||
|
||||
uint32_t ComputedNumberOfChannels(uint32_t aInputChannelCount);
|
||||
|
|
|
@ -156,7 +156,7 @@ DelayBuffer::ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
|
|||
for (uint32_t channel = aFirstChannel;
|
||||
channel < readChannelsEnd; ++channel) {
|
||||
aOutputChunk->ChannelFloatsForWrite(channel)[i] += multiplier *
|
||||
static_cast<const float*>(mUpmixChannels[channel])[readOffset];
|
||||
mUpmixChannels[channel][readOffset];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,24 +238,23 @@ DelayBuffer::UpdateUpmixChannels(int aNewReadChunk, uint32_t aChannelCount,
|
|||
return;
|
||||
}
|
||||
|
||||
static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {};
|
||||
|
||||
NS_WARN_IF_FALSE(mHaveWrittenBlock || aNewReadChunk != mCurrentChunk,
|
||||
"Smoothing is making feedback delay too small.");
|
||||
|
||||
mLastReadChunk = aNewReadChunk;
|
||||
mUpmixChannels = mChunks[aNewReadChunk].mChannelData;
|
||||
mUpmixChannels = mChunks[aNewReadChunk].ChannelData<float>();
|
||||
MOZ_ASSERT(mUpmixChannels.Length() <= aChannelCount);
|
||||
if (mUpmixChannels.Length() < aChannelCount) {
|
||||
if (aChannelInterpretation == ChannelInterpretation::Speakers) {
|
||||
AudioChannelsUpMix(&mUpmixChannels, aChannelCount, silenceChannel);
|
||||
AudioChannelsUpMix(&mUpmixChannels,
|
||||
aChannelCount, SilentChannel::ZeroChannel<float>());
|
||||
MOZ_ASSERT(mUpmixChannels.Length() == aChannelCount,
|
||||
"We called GetAudioChannelsSuperset to avoid this");
|
||||
} else {
|
||||
// Fill up the remaining channels with zeros
|
||||
for (uint32_t channel = mUpmixChannels.Length();
|
||||
channel < aChannelCount; ++channel) {
|
||||
mUpmixChannels.AppendElement(silenceChannel);
|
||||
mUpmixChannels.AppendElement(SilentChannel::ZeroChannel<float>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ private:
|
|||
// Circular buffer for capturing delayed samples.
|
||||
FallibleTArray<AudioChunk> mChunks;
|
||||
// Cache upmixed channel arrays.
|
||||
nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> mUpmixChannels;
|
||||
nsAutoTArray<const float*,GUESS_AUDIO_CHANNELS> mUpmixChannels;
|
||||
double mSmoothingRate;
|
||||
// Current delay, in fractional ticks
|
||||
double mCurrentDelay;
|
||||
|
|
|
@ -23,7 +23,7 @@ async protocol PSpeechSynthesisRequest
|
|||
|
||||
ForceEnd();
|
||||
|
||||
SetAudioOutputVolume(uint32_t aVolume);
|
||||
SetAudioOutputVolume(float aVolume);
|
||||
|
||||
child:
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ SpeechTaskChild::ForceEnd()
|
|||
}
|
||||
|
||||
void
|
||||
SpeechTaskChild::SetAudioOutputVolume(uint32_t aVolume)
|
||||
SpeechTaskChild::SetAudioOutputVolume(float aVolume)
|
||||
{
|
||||
if (mActor) {
|
||||
mActor->SendSetAudioOutputVolume(aVolume);
|
||||
|
|
|
@ -92,7 +92,7 @@ public:
|
|||
|
||||
virtual void ForceEnd() override;
|
||||
|
||||
virtual void SetAudioOutputVolume(uint32_t aVolume) override;
|
||||
virtual void SetAudioOutputVolume(float aVolume) override;
|
||||
|
||||
private:
|
||||
SpeechSynthesisRequestChild* mActor;
|
||||
|
|
|
@ -128,7 +128,7 @@ SpeechSynthesisRequestParent::RecvForceEnd()
|
|||
}
|
||||
|
||||
bool
|
||||
SpeechSynthesisRequestParent::RecvSetAudioOutputVolume(const uint32_t& aVolume)
|
||||
SpeechSynthesisRequestParent::RecvSetAudioOutputVolume(const float& aVolume)
|
||||
{
|
||||
MOZ_ASSERT(mTask);
|
||||
mTask->SetAudioOutputVolume(aVolume);
|
||||
|
|
|
@ -70,7 +70,7 @@ protected:
|
|||
|
||||
virtual bool RecvForceEnd() override;
|
||||
|
||||
virtual bool RecvSetAudioOutputVolume(const uint32_t& aVolume) override;
|
||||
virtual bool RecvSetAudioOutputVolume(const float& aVolume) override;
|
||||
};
|
||||
|
||||
class SpeechTaskParent : public nsSpeechTask
|
||||
|
|
|
@ -12,7 +12,7 @@ typedef unsigned short SpeechServiceType;
|
|||
* required to implement these, although it could be helpful to use the
|
||||
* cancel method for shutting down the speech resources.
|
||||
*/
|
||||
[scriptable, uuid(408251b0-1d7b-4876-888f-718859ce8c9d)]
|
||||
[scriptable, uuid(c576de0c-8a3d-4570-be7e-9876d3e5bed2)]
|
||||
interface nsISpeechTaskCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -29,6 +29,12 @@ interface nsISpeechTaskCallback : nsISupports
|
|||
* The user or application has canceled the speech.
|
||||
*/
|
||||
void onCancel();
|
||||
|
||||
/**
|
||||
* The user or application has changed the volume of this speech.
|
||||
* This is only used on indirect audio service type.
|
||||
*/
|
||||
void onVolumeChanged(in float aVolume);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -667,6 +667,10 @@ nsSpeechTask::CreateAudioChannelAgent()
|
|||
mAudioChannelAgent->InitWithWeakCallback(mUtterance->GetOwner(),
|
||||
static_cast<int32_t>(AudioChannelService::GetDefaultAudioChannel()),
|
||||
this);
|
||||
float volume = 0.0f;
|
||||
bool muted = true;
|
||||
mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY, &volume, &muted);
|
||||
WindowVolumeChanged(volume, muted);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -681,7 +685,7 @@ nsSpeechTask::DestroyAudioChannelAgent()
|
|||
NS_IMETHODIMP
|
||||
nsSpeechTask::WindowVolumeChanged(float aVolume, bool aMuted)
|
||||
{
|
||||
SetAudioOutputVolume(mVolume * aVolume * aMuted);
|
||||
SetAudioOutputVolume(aMuted ? 0.0 : mVolume * aVolume);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -693,11 +697,14 @@ nsSpeechTask::WindowAudioCaptureChanged()
|
|||
}
|
||||
|
||||
void
|
||||
nsSpeechTask::SetAudioOutputVolume(uint32_t aVolume)
|
||||
nsSpeechTask::SetAudioOutputVolume(float aVolume)
|
||||
{
|
||||
if (mStream) {
|
||||
mStream->SetAudioOutputVolume(this, aVolume);
|
||||
}
|
||||
if (mIndirectAudio) {
|
||||
mCallback->OnVolumeChanged(aVolume);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
|
||||
void SetChosenVoiceURI(const nsAString& aUri);
|
||||
|
||||
virtual void SetAudioOutputVolume(uint32_t aVolume);
|
||||
virtual void SetAudioOutputVolume(float aVolume);
|
||||
|
||||
bool IsPreCanceled()
|
||||
{
|
||||
|
|
|
@ -409,6 +409,12 @@ PicoCallbackRunnable::OnCancel()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PicoCallbackRunnable::OnVolumeChanged(float aVolume)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsPicoService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISpeechService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
|
|
|
@ -91,6 +91,11 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnVolumeChanged(float aVolume) override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~FakeSynthCallback() { }
|
||||
|
||||
|
|
|
@ -103,6 +103,13 @@ SapiCallback::OnCancel()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SapiCallback::OnVolumeChanged(float aVolume)
|
||||
{
|
||||
mSapiClient->SetVolume(static_cast<USHORT>(aVolume * 100));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SapiCallback::OnSpeechEvent(const SPEVENT& speechEvent)
|
||||
{
|
||||
|
|
|
@ -249,6 +249,16 @@ nsNPAPIPlugin::PluginCrashed(const nsAString& pluginDumpID,
|
|||
host->PluginCrashed(this, pluginDumpID, browserDumpID);
|
||||
}
|
||||
|
||||
bool
|
||||
nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline PluginLibrary*
|
||||
GetNewPluginLibrary(nsPluginTag *aPluginTag)
|
||||
{
|
||||
|
@ -260,7 +270,10 @@ GetNewPluginLibrary(nsPluginTag *aPluginTag)
|
|||
return PluginModuleContentParent::LoadModule(aPluginTag->mId);
|
||||
}
|
||||
|
||||
return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId, aPluginTag);
|
||||
if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
|
||||
return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId, aPluginTag);
|
||||
}
|
||||
return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary);
|
||||
}
|
||||
|
||||
// Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance).
|
||||
|
|
|
@ -53,6 +53,8 @@ public:
|
|||
void PluginCrashed(const nsAString& pluginDumpID,
|
||||
const nsAString& browserDumpID);
|
||||
|
||||
static bool RunPluginOOP(const nsPluginTag *aPluginTag);
|
||||
|
||||
nsresult Shutdown();
|
||||
|
||||
static nsresult RetainStream(NPStream *pstream, nsISupports **aRetainedPeer);
|
||||
|
|
|
@ -1330,11 +1330,35 @@ nsPluginHost::FindNativePluginForExtension(const nsACString & aExtension,
|
|||
return preferredPlugin;
|
||||
}
|
||||
|
||||
static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
|
||||
nsNPAPIPlugin **aOutNPAPIPlugin)
|
||||
{
|
||||
// If this is an in-process plugin we'll need to load it here if we haven't already.
|
||||
if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
|
||||
if (aPluginTag->mFullPath.IsEmpty())
|
||||
return NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
|
||||
file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath));
|
||||
nsPluginFile pluginFile(file);
|
||||
PRLibrary* pluginLibrary = nullptr;
|
||||
|
||||
if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
aPluginTag->mLibrary = pluginLibrary;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
|
||||
{
|
||||
nsRefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
|
||||
if (!plugin) {
|
||||
nsresult rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, getter_AddRefs(plugin));
|
||||
nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -2232,6 +2256,14 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
|
|||
*aPluginsChanged = true;
|
||||
}
|
||||
|
||||
// Avoid adding different versions of the same plugin if they are running
|
||||
// in-process, otherwise we risk undefined behaviour.
|
||||
if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
|
||||
if (HaveSamePlugin(pluginTag)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't add the same plugin again if it hasn't changed
|
||||
if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
|
||||
if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
|
||||
|
|
|
@ -150,7 +150,7 @@ class RespondWithHandler final : public PromiseNativeHandler
|
|||
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
|
||||
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
|
||||
const RequestMode mRequestMode;
|
||||
const bool mIsClientRequest;
|
||||
const DebugOnly<bool> mIsClientRequest;
|
||||
const bool mIsNavigationRequest;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -272,8 +272,6 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
|||
// If one of the following conditions is true, return a network error:
|
||||
// * response's type is "error".
|
||||
// * request's mode is not "no-cors" and response's type is "opaque".
|
||||
// * request is a client request and response's type is neither "basic"
|
||||
// nor "default".
|
||||
// * request is not a navigation request and response's type is
|
||||
// "opaqueredirect".
|
||||
|
||||
|
@ -282,19 +280,13 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin);
|
||||
|
||||
if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: remove this case as its no longer in the spec (bug 1184967)
|
||||
if (mIsClientRequest && response->Type() != ResponseType::Basic &&
|
||||
response->Type() != ResponseType::Default &&
|
||||
response->Type() != ResponseType::Opaqueredirect) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) {
|
||||
autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION);
|
||||
return;
|
||||
|
|
|
@ -3709,22 +3709,7 @@ public:
|
|||
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
|
||||
NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
uint32_t corsMode;
|
||||
internalChannel->GetCorsMode(&corsMode);
|
||||
switch (corsMode) {
|
||||
case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN:
|
||||
mRequestMode = RequestMode::Same_origin;
|
||||
break;
|
||||
case nsIHttpChannelInternal::CORS_MODE_NO_CORS:
|
||||
mRequestMode = RequestMode::No_cors;
|
||||
break;
|
||||
case nsIHttpChannelInternal::CORS_MODE_CORS:
|
||||
case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT:
|
||||
mRequestMode = RequestMode::Cors;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected CORS mode");
|
||||
}
|
||||
mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
|
||||
|
||||
// This is safe due to static_asserts at top of file.
|
||||
uint32_t redirectMode;
|
||||
|
@ -3757,6 +3742,8 @@ public:
|
|||
|
||||
mMethod = "GET";
|
||||
|
||||
mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
|
||||
|
||||
if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
|
||||
mRequestCredentials = RequestCredentials::Omit;
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@ self.addEventListener("install", function(event) {
|
|||
event.waitUntil(
|
||||
self.caches.open("origin-cache")
|
||||
.then(c => {
|
||||
return c.add(new Request(prefix + 'index-https.sjs',
|
||||
{ redirect: 'manual' }));
|
||||
return c.add(prefix + 'index-https.sjs');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Access-Control-Allow-Origin: https://example.com
|
|
@ -6,10 +6,8 @@ self.addEventListener("install", function(event) {
|
|||
.then(c => {
|
||||
return Promise.all(
|
||||
[
|
||||
c.add(new Request(prefix + 'index.sjs',
|
||||
{ redirect: 'manual' } )),
|
||||
c.add(new Request(prefix + 'index-to-https.sjs',
|
||||
{ redirect: 'manual' } ))
|
||||
c.add(prefix + 'index.sjs'),
|
||||
c.add(prefix + 'index-to-https.sjs')
|
||||
]
|
||||
);
|
||||
})
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Access-Control-Allow-Origin: http://mochi.test:8888
|
|
@ -53,11 +53,13 @@ support-files =
|
|||
fetch/origin/index.sjs
|
||||
fetch/origin/index-to-https.sjs
|
||||
fetch/origin/realindex.html
|
||||
fetch/origin/realindex.html^headers^
|
||||
fetch/origin/register.html
|
||||
fetch/origin/unregister.html
|
||||
fetch/origin/origin_test.js
|
||||
fetch/origin/https/index-https.sjs
|
||||
fetch/origin/https/realindex.html
|
||||
fetch/origin/https/realindex.html^headers^
|
||||
fetch/origin/https/register.html
|
||||
fetch/origin/https/unregister.html
|
||||
fetch/origin/https/origin_test.js
|
||||
|
|
|
@ -270,28 +270,11 @@ IMFYCbCrImage::GetTextureClient(CompositableClient* aClient)
|
|||
mData.mCbCrStride, mData.mCbCrStride * mData.mCbCrSize.height);
|
||||
}
|
||||
|
||||
RefPtr<IDXGIResource> resource;
|
||||
|
||||
HANDLE shareHandleY;
|
||||
textureY->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
hr = resource->GetSharedHandle(&shareHandleY);
|
||||
|
||||
HANDLE shareHandleCb;
|
||||
textureCb->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
hr = resource->GetSharedHandle(&shareHandleCb);
|
||||
|
||||
HANDLE shareHandleCr;
|
||||
textureCr->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
hr = resource->GetSharedHandle(&shareHandleCr);
|
||||
|
||||
mTextureClient = DXGIYCbCrTextureClient::Create(aClient->GetForwarder(),
|
||||
TextureFlags::DEFAULT,
|
||||
textureY,
|
||||
textureCb,
|
||||
textureCr,
|
||||
shareHandleY,
|
||||
shareHandleCb,
|
||||
shareHandleCr,
|
||||
GetSize(),
|
||||
mData.mYSize,
|
||||
mData.mCbCrSize);
|
||||
|
|
|
@ -170,6 +170,7 @@ ImageContainer::ImageContainer(Mode flag)
|
|||
: mReentrantMonitor("ImageContainer.mReentrantMonitor"),
|
||||
mGenerationCounter(++sGenerationCounter),
|
||||
mPaintCount(0),
|
||||
mAttachCount(0),
|
||||
mDroppedImageCount(0),
|
||||
mImageFactory(new ImageFactory()),
|
||||
mRecycleBin(new BufferRecycleBin()),
|
||||
|
@ -234,6 +235,28 @@ ImageContainer::CreateImage(ImageFormat aFormat)
|
|||
return mImageFactory->CreateImage(aFormat, mScaleHint, mRecycleBin);
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::NotifyAttached()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (!mAttachCount++) {
|
||||
if (IsAsync()) {
|
||||
ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::NotifyDetached()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (!--mAttachCount) {
|
||||
if (IsAsync()) {
|
||||
ImageBridgeChild::FlushAllImagesAsync(mImageClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
|
||||
{
|
||||
|
|
|
@ -388,6 +388,18 @@ public:
|
|||
*/
|
||||
uint64_t GetAsyncContainerID() const;
|
||||
|
||||
/**
|
||||
* We track when ImageContainers are attached to layers so that we can
|
||||
* avoid sending images through ImageBridge if they won't be displayed.
|
||||
*/
|
||||
void NotifyAttached();
|
||||
void NotifyDetached();
|
||||
|
||||
bool IsAttached() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return !!mAttachCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the container currently has an image.
|
||||
* Can be called on any thread. This method takes mReentrantMonitor
|
||||
|
@ -519,6 +531,8 @@ private:
|
|||
// threadsafe.
|
||||
uint32_t mPaintCount;
|
||||
|
||||
int32_t mAttachCount;
|
||||
|
||||
// See GetPaintDelay. Accessed only with mReentrantMonitor held.
|
||||
TimeDuration mPaintDelay;
|
||||
|
||||
|
|
|
@ -19,11 +19,25 @@ ImageLayer::ImageLayer(LayerManager* aManager, void* aImplData)
|
|||
{}
|
||||
|
||||
ImageLayer::~ImageLayer()
|
||||
{}
|
||||
{
|
||||
if (mContainer) {
|
||||
mContainer->NotifyDetached();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageLayer::SetContainer(ImageContainer* aContainer)
|
||||
{
|
||||
if (aContainer == mContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mContainer) {
|
||||
mContainer->NotifyDetached();
|
||||
}
|
||||
mContainer = aContainer;
|
||||
if (mContainer) {
|
||||
mContainer->NotifyAttached();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageLayer::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
|
||||
|
|
|
@ -667,9 +667,9 @@ DXGIYCbCrTextureClient::~DXGIYCbCrTextureClient()
|
|||
already_AddRefed<DXGIYCbCrTextureClient>
|
||||
DXGIYCbCrTextureClient::Create(ISurfaceAllocator* aAllocator,
|
||||
TextureFlags aFlags,
|
||||
IUnknown* aTextureY,
|
||||
IUnknown* aTextureCb,
|
||||
IUnknown* aTextureCr,
|
||||
IDirect3DTexture9* aTextureY,
|
||||
IDirect3DTexture9* aTextureCb,
|
||||
IDirect3DTexture9* aTextureCr,
|
||||
HANDLE aHandleY,
|
||||
HANDLE aHandleCb,
|
||||
HANDLE aHandleCr,
|
||||
|
@ -682,6 +682,13 @@ DXGIYCbCrTextureClient::Create(ISurfaceAllocator* aAllocator,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
aTextureY->SetPrivateData(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(aSizeY.width * aSizeY.height), sizeof(IUnknown*), D3DSPD_IUNKNOWN);
|
||||
aTextureCb->SetPrivateData(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height), sizeof(IUnknown*), D3DSPD_IUNKNOWN);
|
||||
aTextureCr->SetPrivateData(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height), sizeof(IUnknown*), D3DSPD_IUNKNOWN);
|
||||
|
||||
RefPtr<DXGIYCbCrTextureClient> texture =
|
||||
new DXGIYCbCrTextureClient(aAllocator, aFlags);
|
||||
texture->mHandles[0] = aHandleY;
|
||||
|
@ -696,6 +703,59 @@ DXGIYCbCrTextureClient::Create(ISurfaceAllocator* aAllocator,
|
|||
return texture.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DXGIYCbCrTextureClient>
|
||||
DXGIYCbCrTextureClient::Create(ISurfaceAllocator* aAllocator,
|
||||
TextureFlags aFlags,
|
||||
ID3D11Texture2D* aTextureY,
|
||||
ID3D11Texture2D* aTextureCb,
|
||||
ID3D11Texture2D* aTextureCr,
|
||||
const gfx::IntSize& aSize,
|
||||
const gfx::IntSize& aSizeY,
|
||||
const gfx::IntSize& aSizeCbCr)
|
||||
{
|
||||
if (!aTextureY || !aTextureCb || !aTextureCr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aTextureY->SetPrivateDataInterface(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(aSize.width * aSize.height));
|
||||
aTextureCb->SetPrivateDataInterface(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
|
||||
aTextureCr->SetPrivateDataInterface(sD3D11TextureUsage,
|
||||
new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
|
||||
|
||||
RefPtr<DXGIYCbCrTextureClient> texture =
|
||||
new DXGIYCbCrTextureClient(aAllocator, aFlags);
|
||||
|
||||
RefPtr<IDXGIResource> resource;
|
||||
|
||||
aTextureY->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
HRESULT hr = resource->GetSharedHandle(&texture->mHandles[0]);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aTextureCb->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
hr = resource->GetSharedHandle(&texture->mHandles[1]);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aTextureCr->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
hr = resource->GetSharedHandle(&texture->mHandles[2]);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
texture->mHoldRefs[0] = aTextureY;
|
||||
texture->mHoldRefs[1] = aTextureCb;
|
||||
texture->mHoldRefs[2] = aTextureCr;
|
||||
texture->mSize = aSize;
|
||||
texture->mSizeY = aSizeY;
|
||||
texture->mSizeCbCr = aSizeCbCr;
|
||||
return texture.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
DXGIYCbCrTextureClient::Lock(OpenMode)
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "gfxWindowsPlatform.h"
|
||||
#include "mozilla/GfxMessageUtils.h"
|
||||
#include <d3d11.h>
|
||||
#include "d3d9.h"
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -109,9 +110,9 @@ public:
|
|||
static already_AddRefed<DXGIYCbCrTextureClient>
|
||||
Create(ISurfaceAllocator* aAllocator,
|
||||
TextureFlags aFlags,
|
||||
IUnknown* aTextureY,
|
||||
IUnknown* aTextureCb,
|
||||
IUnknown* aTextureCr,
|
||||
IDirect3DTexture9* aTextureY,
|
||||
IDirect3DTexture9* aTextureCb,
|
||||
IDirect3DTexture9* aTextureCr,
|
||||
HANDLE aHandleY,
|
||||
HANDLE aHandleCb,
|
||||
HANDLE aHandleCr,
|
||||
|
@ -119,6 +120,17 @@ public:
|
|||
const gfx::IntSize& aSizeY,
|
||||
const gfx::IntSize& aSizeCbCr);
|
||||
|
||||
// Creates a TextureClient and init width.
|
||||
static already_AddRefed<DXGIYCbCrTextureClient>
|
||||
Create(ISurfaceAllocator* aAllocator,
|
||||
TextureFlags aFlags,
|
||||
ID3D11Texture2D* aTextureY,
|
||||
ID3D11Texture2D* aTextureCb,
|
||||
ID3D11Texture2D* aTextureCr,
|
||||
const gfx::IntSize& aSize,
|
||||
const gfx::IntSize& aSizeY,
|
||||
const gfx::IntSize& aSizeCbCr);
|
||||
|
||||
// TextureClient
|
||||
|
||||
virtual bool IsAllocated() const override{ return !!mHoldRefs[0]; }
|
||||
|
|
|
@ -407,6 +407,9 @@ static void UpdateImageClientNow(ImageClient* aClient, ImageContainer* aContaine
|
|||
{
|
||||
MOZ_ASSERT(aClient);
|
||||
MOZ_ASSERT(aContainer);
|
||||
if (!aContainer->IsAttached()) {
|
||||
return;
|
||||
}
|
||||
sImageBridgeChildSingleton->BeginTransaction();
|
||||
aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
|
||||
sImageBridgeChildSingleton->EndTransaction();
|
||||
|
@ -475,6 +478,33 @@ void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
|
|||
waiter->WaitComplete();
|
||||
}
|
||||
|
||||
static void FlushAllImagesAsyncInternal(ImageClient* aClient)
|
||||
{
|
||||
MOZ_ASSERT(aClient);
|
||||
sImageBridgeChildSingleton->BeginTransaction();
|
||||
aClient->FlushAllImages(nullptr);
|
||||
sImageBridgeChildSingleton->EndTransaction();
|
||||
}
|
||||
|
||||
//static
|
||||
void ImageBridgeChild::FlushAllImagesAsync(ImageClient* aClient)
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(aClient);
|
||||
MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
|
||||
MOZ_ASSERT(!InImageBridgeChildThread());
|
||||
if (InImageBridgeChildThread()) {
|
||||
NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
|
||||
return;
|
||||
}
|
||||
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(&FlushAllImagesAsyncInternal, aClient));
|
||||
}
|
||||
|
||||
void
|
||||
ImageBridgeChild::BeginTransaction()
|
||||
{
|
||||
|
|
|
@ -221,6 +221,8 @@ public:
|
|||
*/
|
||||
static void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
|
||||
|
||||
static void FlushAllImagesAsync(ImageClient* aClient);
|
||||
|
||||
// CompositableForwarder
|
||||
|
||||
virtual void Connect(CompositableClient* aCompositable,
|
||||
|
|
|
@ -319,7 +319,7 @@ AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembl
|
|||
MOZ_ASSERT(masm.jumpRelocationTableBytes() == 0);
|
||||
MOZ_ASSERT(masm.dataRelocationTableBytes() == 0);
|
||||
MOZ_ASSERT(masm.preBarrierTableBytes() == 0);
|
||||
MOZ_ASSERT(!masm.hasEnteredExitFrame());
|
||||
MOZ_ASSERT(!masm.hasSelfReference());
|
||||
|
||||
// Copy over metadata, making sure to update all offsets on ARM.
|
||||
|
||||
|
|
|
@ -1367,7 +1367,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
return hn;
|
||||
}
|
||||
static bool match(const ExitDescriptor& lhs, const ExitDescriptor& rhs) {
|
||||
return lhs.name_ == rhs.name_ && lhs.sig_ == rhs.sig_;
|
||||
return lhs.name_ == rhs.name_ && *lhs.sig_ == *rhs.sig_;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11453,7 +11453,7 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit
|
|||
}
|
||||
|
||||
AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
|
||||
masm.callJitFromAsmJS(callee);
|
||||
masm.callJitNoProfiler(callee);
|
||||
AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
|
||||
|
||||
{
|
||||
|
|
|
@ -821,7 +821,9 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode* opn)
|
|||
ParseNode* pn2;
|
||||
if (opn->isKind(PNK_OBJECT)) {
|
||||
if (opn2->isKind(PNK_MUTATEPROTO)) {
|
||||
ParseNode* target = cloneLeftHandSide(opn2->pn_kid);
|
||||
ParseNode* target = opn2->pn_kid->isKind(PNK_ASSIGN)
|
||||
? cloneDestructuringDefault(opn2->pn_kid)
|
||||
: cloneLeftHandSide(opn2->pn_kid);
|
||||
if (!target)
|
||||
return nullptr;
|
||||
pn2 = handler.new_<UnaryNode>(PNK_MUTATEPROTO, JSOP_NOP, opn2->pn_pos, target);
|
||||
|
@ -832,12 +834,9 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode* opn)
|
|||
ParseNode* tag = cloneParseTree(opn2->pn_left);
|
||||
if (!tag)
|
||||
return nullptr;
|
||||
ParseNode* target;
|
||||
if (opn2->pn_right->isKind(PNK_ASSIGN)) {
|
||||
target = cloneDestructuringDefault(opn2->pn_right);
|
||||
} else {
|
||||
target = cloneLeftHandSide(opn2->pn_right);
|
||||
}
|
||||
ParseNode* target = opn2->pn_right->isKind(PNK_ASSIGN)
|
||||
? cloneDestructuringDefault(opn2->pn_right)
|
||||
: cloneLeftHandSide(opn2->pn_right);
|
||||
if (!target)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -1397,7 +1397,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
|||
JSFunction::Flags flags;
|
||||
switch (kind) {
|
||||
case Expression:
|
||||
flags = JSFunction::INTERPRETED_LAMBDA;
|
||||
flags = (generatorKind == NotGenerator
|
||||
? JSFunction::INTERPRETED_LAMBDA
|
||||
: JSFunction::INTERPRETED_LAMBDA_GENERATOR);
|
||||
break;
|
||||
case Arrow:
|
||||
flags = JSFunction::INTERPRETED_LAMBDA_ARROW;
|
||||
|
@ -1405,10 +1407,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
|||
break;
|
||||
case Method:
|
||||
MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
|
||||
if (generatorKind == NotGenerator)
|
||||
flags = JSFunction::INTERPRETED_METHOD;
|
||||
else
|
||||
flags = JSFunction::INTERPRETED_METHOD_GENERATOR;
|
||||
flags = (generatorKind == NotGenerator
|
||||
? JSFunction::INTERPRETED_METHOD
|
||||
: JSFunction::INTERPRETED_METHOD_GENERATOR);
|
||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||
break;
|
||||
case ClassConstructor:
|
||||
|
@ -1425,8 +1426,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
|||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||
break;
|
||||
default:
|
||||
flags = JSFunction::INTERPRETED_NORMAL;
|
||||
break;
|
||||
flags = (generatorKind == NotGenerator
|
||||
? JSFunction::INTERPRETED_NORMAL
|
||||
: JSFunction::INTERPRETED_GENERATOR);
|
||||
}
|
||||
|
||||
fun = NewFunctionWithProto(context, nullptr, 0, flags, nullptr, atom, proto,
|
||||
|
|
|
@ -3786,7 +3786,11 @@ BaselineCompiler::emit_JSOP_RESUME()
|
|||
// Push a fake return address on the stack. We will resume here when the
|
||||
// generator returns.
|
||||
Label genStart, returnTarget;
|
||||
#ifdef JS_USE_LINK_REGISTER
|
||||
masm.call(&genStart);
|
||||
#else
|
||||
masm.callAndPushReturnAddress(&genStart);
|
||||
#endif
|
||||
|
||||
// Add an IC entry so the return offset -> pc mapping works.
|
||||
if (!appendICEntry(ICEntry::Kind_Op, masm.currentOffset()))
|
||||
|
@ -3794,6 +3798,9 @@ BaselineCompiler::emit_JSOP_RESUME()
|
|||
|
||||
masm.jump(&returnTarget);
|
||||
masm.bind(&genStart);
|
||||
#ifdef JS_USE_LINK_REGISTER
|
||||
masm.pushReturnAddress();
|
||||
#endif
|
||||
|
||||
// If profiler instrumentation is on, update lastProfilingFrame on
|
||||
// current JitActivation
|
||||
|
|
|
@ -10365,7 +10365,7 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
EmitBaselineCreateStubFrameDescriptor(masm, scratch);
|
||||
masm.push(scratch);
|
||||
masm.push(ICTailCallReg);
|
||||
masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(NativeExitFrameLayoutToken);
|
||||
|
||||
// Execute call.
|
||||
masm.setupUnalignedABICall(scratch);
|
||||
|
@ -10463,7 +10463,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
EmitBaselineCreateStubFrameDescriptor(masm, scratch);
|
||||
masm.push(scratch);
|
||||
masm.push(ICTailCallReg);
|
||||
masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(NativeExitFrameLayoutToken);
|
||||
|
||||
// Execute call.
|
||||
masm.setupUnalignedABICall(scratch);
|
||||
|
|
|
@ -2872,9 +2872,8 @@ CodeGenerator::visitCallNative(LCallNative* call)
|
|||
masm.Push(argUintNReg);
|
||||
|
||||
// Construct native exit frame.
|
||||
uint32_t safepointOffset;
|
||||
masm.buildFakeExitFrame(tempReg, &safepointOffset);
|
||||
masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
|
||||
uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
|
||||
masm.enterFakeExitFrame(NativeExitFrameLayoutToken);
|
||||
|
||||
markSafepointAt(safepointOffset, call);
|
||||
|
||||
|
@ -2991,9 +2990,8 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call)
|
|||
masm.moveStackPtrTo(argObj);
|
||||
|
||||
// Construct native exit frame.
|
||||
uint32_t safepointOffset;
|
||||
masm.buildFakeExitFrame(argJSContext, &safepointOffset);
|
||||
masm.enterFakeExitFrame(IonDOMMethodExitFrameLayout::Token());
|
||||
uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
|
||||
masm.enterFakeExitFrame(IonDOMMethodExitFrameLayoutToken);
|
||||
|
||||
markSafepointAt(safepointOffset, call);
|
||||
|
||||
|
@ -6313,7 +6311,7 @@ JitRuntime::generateLazyLinkStub(JSContext* cx)
|
|||
size_t convertToExitFrame = JitFrameLayout::Size() - ExitFrameLayout::Size();
|
||||
masm.addPtr(Imm32(convertToExitFrame << FRAMESIZE_SHIFT), descriptor);
|
||||
|
||||
masm.enterFakeExitFrame(LazyLinkExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(LazyLinkExitFrameLayoutToken);
|
||||
masm.PushStubCode();
|
||||
|
||||
masm.setupUnalignedABICall(temp0);
|
||||
|
@ -9735,9 +9733,8 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins)
|
|||
// Rooting will happen at GC time.
|
||||
masm.moveStackPtrTo(ObjectReg);
|
||||
|
||||
uint32_t safepointOffset;
|
||||
masm.buildFakeExitFrame(JSContextReg, &safepointOffset);
|
||||
masm.enterFakeExitFrame(IonDOMExitFrameLayout::GetterToken());
|
||||
uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
|
||||
masm.enterFakeExitFrame(IonDOMExitFrameLayoutGetterToken);
|
||||
|
||||
markSafepointAt(safepointOffset, ins);
|
||||
|
||||
|
@ -9825,9 +9822,8 @@ CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins)
|
|||
// Rooting will happen at GC time.
|
||||
masm.moveStackPtrTo(ObjectReg);
|
||||
|
||||
uint32_t safepointOffset;
|
||||
masm.buildFakeExitFrame(JSContextReg, &safepointOffset);
|
||||
masm.enterFakeExitFrame(IonDOMExitFrameLayout::SetterToken());
|
||||
uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
|
||||
masm.enterFakeExitFrame(IonDOMExitFrameLayoutSetterToken);
|
||||
|
||||
markSafepointAt(safepointOffset, ins);
|
||||
|
||||
|
|
|
@ -945,7 +945,7 @@ EmitGetterCall(JSContext* cx, MacroAssembler& masm,
|
|||
|
||||
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
|
||||
return false;
|
||||
masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(IonOOLNativeExitFrameLayoutToken);
|
||||
|
||||
// Construct and execute call.
|
||||
masm.setupUnalignedABICall(scratchReg);
|
||||
|
@ -1003,7 +1003,7 @@ EmitGetterCall(JSContext* cx, MacroAssembler& masm,
|
|||
|
||||
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
|
||||
return false;
|
||||
masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayoutToken);
|
||||
|
||||
// Make the call.
|
||||
masm.setupUnalignedABICall(scratchReg);
|
||||
|
@ -1586,7 +1586,7 @@ EmitCallProxyGet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& at
|
|||
|
||||
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
|
||||
return false;
|
||||
masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(IonOOLProxyExitFrameLayoutToken);
|
||||
|
||||
// Make the call.
|
||||
masm.setupUnalignedABICall(scratch);
|
||||
|
@ -2297,7 +2297,7 @@ EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& at
|
|||
|
||||
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
|
||||
return false;
|
||||
masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(IonOOLProxyExitFrameLayoutToken);
|
||||
|
||||
// Make the call.
|
||||
masm.setupUnalignedABICall(scratch);
|
||||
|
@ -2506,7 +2506,7 @@ GenerateCallSetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
|||
|
||||
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
|
||||
return false;
|
||||
masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(IonOOLNativeExitFrameLayoutToken);
|
||||
|
||||
// Make the call
|
||||
masm.setupUnalignedABICall(scratchReg);
|
||||
|
@ -2570,7 +2570,7 @@ GenerateCallSetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
|||
|
||||
if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
|
||||
return false;
|
||||
masm.enterFakeExitFrame(IonOOLSetterOpExitFrameLayout::Token());
|
||||
masm.enterFakeExitFrame(IonOOLSetterOpExitFrameLayoutToken);
|
||||
|
||||
// Make the call.
|
||||
masm.setupUnalignedABICall(scratchReg);
|
||||
|
|
|
@ -4358,26 +4358,39 @@ MaybeUnwrapElements(const MDefinition* elementsOrObj)
|
|||
return elementsOrObj->toElements();
|
||||
}
|
||||
|
||||
static inline const MDefinition*
|
||||
GetElementsObject(const MDefinition* elementsOrObj)
|
||||
{
|
||||
if (elementsOrObj->type() == MIRType_Object)
|
||||
return elementsOrObj;
|
||||
|
||||
const MDefinition* elements = MaybeUnwrapElements(elementsOrObj);
|
||||
if (elements)
|
||||
return elements->toElements()->input();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Gets the MDefinition of the target Object for the given store operation.
|
||||
static inline const MDefinition*
|
||||
GetStoreObject(const MDefinition* store)
|
||||
{
|
||||
switch (store->op()) {
|
||||
case MDefinition::Op_StoreElement: {
|
||||
const MDefinition* elementsOrObj = store->toStoreElement()->elements();
|
||||
if (elementsOrObj->type() == MIRType_Object)
|
||||
return elementsOrObj;
|
||||
|
||||
const MDefinition* elements = MaybeUnwrapElements(elementsOrObj);
|
||||
if (elements)
|
||||
return elements->toElements()->input();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
case MDefinition::Op_StoreElement:
|
||||
return GetElementsObject(store->toStoreElement()->elements());
|
||||
|
||||
case MDefinition::Op_StoreElementHole:
|
||||
return store->toStoreElementHole()->object();
|
||||
|
||||
case MDefinition::Op_StoreUnboxedObjectOrNull:
|
||||
return GetElementsObject(store->toStoreUnboxedObjectOrNull()->elements());
|
||||
|
||||
case MDefinition::Op_StoreUnboxedString:
|
||||
return GetElementsObject(store->toStoreUnboxedString()->elements());
|
||||
|
||||
case MDefinition::Op_StoreUnboxedScalar:
|
||||
return GetElementsObject(store->toStoreUnboxedScalar()->elements());
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4438,6 +4451,30 @@ MInitializedLength::mightAlias(const MDefinition* store) const
|
|||
return GenericLoadMightAlias(elements(), store);
|
||||
}
|
||||
|
||||
bool
|
||||
MLoadUnboxedObjectOrNull::mightAlias(const MDefinition* store) const
|
||||
{
|
||||
return GenericLoadMightAlias(elements(), store);
|
||||
}
|
||||
|
||||
bool
|
||||
MLoadUnboxedString::mightAlias(const MDefinition* store) const
|
||||
{
|
||||
return GenericLoadMightAlias(elements(), store);
|
||||
}
|
||||
|
||||
bool
|
||||
MLoadUnboxedScalar::mightAlias(const MDefinition* store) const
|
||||
{
|
||||
return GenericLoadMightAlias(elements(), store);
|
||||
}
|
||||
|
||||
bool
|
||||
MUnboxedArrayInitializedLength::mightAlias(const MDefinition* store) const
|
||||
{
|
||||
return GenericLoadMightAlias(object(), store);
|
||||
}
|
||||
|
||||
bool
|
||||
MGuardReceiverPolymorphic::congruentTo(const MDefinition* ins) const
|
||||
{
|
||||
|
@ -4988,15 +5025,46 @@ PropertyReadNeedsTypeBarrier(CompilerConstraintList* constraints,
|
|||
return BarrierKind::NoBarrier;
|
||||
}
|
||||
|
||||
static bool
|
||||
ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second)
|
||||
{
|
||||
if (first->isSingleton() ||
|
||||
second->isSingleton() ||
|
||||
first->clasp() != second->clasp() ||
|
||||
first->unknownProperties() ||
|
||||
second->unknownProperties())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (first->clasp() == &ArrayObject::class_) {
|
||||
HeapTypeSetKey firstElements = first->property(JSID_VOID);
|
||||
HeapTypeSetKey secondElements = second->property(JSID_VOID);
|
||||
|
||||
return firstElements.maybeTypes() && secondElements.maybeTypes() &&
|
||||
firstElements.maybeTypes()->equals(secondElements.maybeTypes());
|
||||
}
|
||||
|
||||
if (first->clasp() == &UnboxedArrayObject::class_) {
|
||||
return first->group()->unboxedLayout().elementType() ==
|
||||
second->group()->unboxedLayout().elementType();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BarrierKind
|
||||
jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
|
||||
CompilerConstraintList* constraints,
|
||||
TypeSet::ObjectKey* key, PropertyName* name,
|
||||
TemporaryTypeSet* observed, bool updateObserved)
|
||||
{
|
||||
if (!updateObserved)
|
||||
return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
|
||||
|
||||
// If this access has never executed, try to add types to the observed set
|
||||
// according to any property which exists on the object or its prototype.
|
||||
if (updateObserved && observed->empty() && name) {
|
||||
if (observed->empty() && name) {
|
||||
JSObject* obj;
|
||||
if (key->isSingleton())
|
||||
obj = key->singleton();
|
||||
|
@ -5029,6 +5097,30 @@ jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
|
|||
}
|
||||
}
|
||||
|
||||
// If any objects which could be observed are similar to ones that have
|
||||
// already been observed, add them to the observed type set.
|
||||
if (!key->unknownProperties()) {
|
||||
HeapTypeSetKey property = key->property(name ? NameToId(name) : JSID_VOID);
|
||||
|
||||
if (property.maybeTypes() && !property.maybeTypes()->unknownObject()) {
|
||||
for (size_t i = 0; i < property.maybeTypes()->getObjectCount(); i++) {
|
||||
TypeSet::ObjectKey* key = property.maybeTypes()->getObject(i);
|
||||
if (!key || observed->unknownObject())
|
||||
continue;
|
||||
|
||||
for (size_t j = 0; j < observed->getObjectCount(); j++) {
|
||||
TypeSet::ObjectKey* observedKey = observed->getObject(j);
|
||||
if (observedKey && ObjectSubsumes(observedKey, key)) {
|
||||
// Note: the return value here is ignored.
|
||||
observed->addType(TypeSet::ObjectType(key),
|
||||
GetJitContext()->temp->lifoAlloc());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
|
||||
}
|
||||
|
||||
|
|
|
@ -8008,6 +8008,7 @@ class MUnboxedArrayInitializedLength
|
|||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
bool mightAlias(const MDefinition* store) const override;
|
||||
|
||||
ALLOW_CLONE(MUnboxedArrayInitializedLength)
|
||||
};
|
||||
|
@ -8761,6 +8762,7 @@ class MLoadUnboxedObjectOrNull
|
|||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::UnboxedElement);
|
||||
}
|
||||
bool mightAlias(const MDefinition* store) const override;
|
||||
|
||||
ALLOW_CLONE(MLoadUnboxedObjectOrNull)
|
||||
};
|
||||
|
@ -8810,6 +8812,7 @@ class MLoadUnboxedString
|
|||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::UnboxedElement);
|
||||
}
|
||||
bool mightAlias(const MDefinition* store) const override;
|
||||
|
||||
ALLOW_CLONE(MLoadUnboxedString)
|
||||
};
|
||||
|
@ -9462,6 +9465,7 @@ class MLoadUnboxedScalar
|
|||
return AliasSet::Store(AliasSet::UnboxedElement);
|
||||
return AliasSet::Load(AliasSet::UnboxedElement);
|
||||
}
|
||||
bool mightAlias(const MDefinition* store) const override;
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
if (requiresBarrier_)
|
||||
|
@ -12900,7 +12904,7 @@ class MRecompileCheck : public MNullaryInstruction
|
|||
|
||||
// All barriered operations - MMemoryBarrier, MCompareExchangeTypedArrayElement,
|
||||
// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as well as
|
||||
// MLoadUnboxedScalar and MStoreUnboxedSclaar when they are marked as requiring
|
||||
// MLoadUnboxedScalar and MStoreUnboxedScalar when they are marked as requiring
|
||||
// a memory barrer - have the following attributes:
|
||||
//
|
||||
// - Not movable
|
||||
|
|
|
@ -94,9 +94,8 @@ MacroAssembler::passABIArg(FloatRegister reg, MoveOp::Type type)
|
|||
template <typename T> void
|
||||
MacroAssembler::callWithABI(const T& fun, MoveOp::Type result)
|
||||
{
|
||||
profilerPreCall();
|
||||
AutoProfilerCallInstrumentation profiler(*this);
|
||||
callWithABINoProfiler(fun, result);
|
||||
profilerPostReturn();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -152,30 +151,92 @@ MacroAssembler::signature() const
|
|||
#endif
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
// Jit Frames.
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::callJitNoProfiler(Register callee)
|
||||
{
|
||||
#ifdef JS_USE_LINK_REGISTER
|
||||
// The return address is pushed by the callee.
|
||||
call(callee);
|
||||
#else
|
||||
callAndPushReturnAddress(callee);
|
||||
#endif
|
||||
return currentOffset();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::callJit(Register callee)
|
||||
{
|
||||
AutoProfilerCallInstrumentation profiler(*this);
|
||||
uint32_t ret = callJitNoProfiler(callee);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::callJit(JitCode* callee)
|
||||
{
|
||||
AutoProfilerCallInstrumentation profiler(*this);
|
||||
call(callee);
|
||||
return currentOffset();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::makeFrameDescriptor(Register frameSizeReg, FrameType type)
|
||||
{
|
||||
// See JitFrames.h for a description of the frame descriptor format.
|
||||
|
||||
lshiftPtr(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
|
||||
// The saved-frame bit is zero for new frames. See js::SavedStacks.
|
||||
orPtr(Imm32(type), frameSizeReg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushStaticFrameDescriptor(FrameType type)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(framePushed(), type);
|
||||
Push(Imm32(descriptor));
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::buildFakeExitFrame(Register scratch)
|
||||
{
|
||||
mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
|
||||
|
||||
pushStaticFrameDescriptor(JitFrame_IonJS);
|
||||
uint32_t retAddr = pushFakeReturnAddress(scratch);
|
||||
|
||||
MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
|
||||
return retAddr;
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Exit frame footer.
|
||||
|
||||
void
|
||||
MacroAssembler::PushStubCode()
|
||||
{
|
||||
exitCodePatch_ = PushWithPatch(ImmWord(-1));
|
||||
// Make sure that we do not erase an existing self-reference.
|
||||
MOZ_ASSERT(!hasSelfReference());
|
||||
selfReferencePatch_ = PushWithPatch(ImmWord(-1));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::enterExitFrame(const VMFunction* f)
|
||||
{
|
||||
linkExitFrame();
|
||||
// Push the ioncode. (Bailout or VM wrapper)
|
||||
// Push the JitCode pointer. (Keep the code alive, when on the stack)
|
||||
PushStubCode();
|
||||
// Push VMFunction pointer, to mark arguments.
|
||||
Push(ImmPtr(f));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::enterFakeExitFrame(JitCode* codeVal)
|
||||
MacroAssembler::enterFakeExitFrame(enum ExitFrameTokenValues token)
|
||||
{
|
||||
linkExitFrame();
|
||||
Push(ImmPtr(codeVal));
|
||||
Push(Imm32(token));
|
||||
Push(ImmPtr(nullptr));
|
||||
}
|
||||
|
||||
|
@ -186,11 +247,14 @@ MacroAssembler::leaveExitFrame(size_t extraFrame)
|
|||
}
|
||||
|
||||
bool
|
||||
MacroAssembler::hasEnteredExitFrame() const
|
||||
MacroAssembler::hasSelfReference() const
|
||||
{
|
||||
return exitCodePatch_.offset() != 0;
|
||||
return selfReferencePatch_.offset() != 0;
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -1588,15 +1588,6 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output)
|
|||
bind(&done);
|
||||
}
|
||||
|
||||
// Save an exit frame (which must be aligned to the stack pointer) to
|
||||
// PerThreadData::jitTop of the main thread.
|
||||
void
|
||||
MacroAssembler::linkExitFrame()
|
||||
{
|
||||
AbsoluteAddress jitTop(GetJitContext()->runtime->addressOfJitTop());
|
||||
storeStackPtr(jitTop);
|
||||
}
|
||||
|
||||
static void
|
||||
BailoutReportOverRecursed(JSContext* cx)
|
||||
{
|
||||
|
@ -1667,7 +1658,7 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
|
|||
push(temp);
|
||||
push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
|
||||
// No GC things to mark on the stack, push a bare token.
|
||||
enterFakeExitFrame(ExitFrameLayout::BareToken());
|
||||
enterFakeExitFrame(ExitFrameLayoutBareToken);
|
||||
|
||||
// If monitorStub is non-null, handle resumeAddr appropriately.
|
||||
Label noMonitor;
|
||||
|
@ -2365,24 +2356,8 @@ void
|
|||
MacroAssembler::link(JitCode* code)
|
||||
{
|
||||
MOZ_ASSERT(!oom());
|
||||
// If this code can transition to C++ code and witness a GC, then we need to store
|
||||
// the JitCode onto the stack in order to GC it correctly. exitCodePatch should
|
||||
// be unset if the code never needed to push its JitCode*.
|
||||
if (hasEnteredExitFrame()) {
|
||||
exitCodePatch_.fixup(this);
|
||||
PatchDataWithValueCheck(CodeLocationLabel(code, exitCodePatch_),
|
||||
ImmPtr(code),
|
||||
ImmPtr((void*)-1));
|
||||
}
|
||||
|
||||
// Fix up the code pointers to be written for locations where profilerCallSite
|
||||
// emitted moves of RIP to a register.
|
||||
for (size_t i = 0; i < profilerCallSites_.length(); i++) {
|
||||
CodeOffsetLabel offset = profilerCallSites_[i];
|
||||
offset.fixup(this);
|
||||
CodeLocationLabel location(code, offset);
|
||||
PatchDataWithValueCheck(location, ImmPtr(location.raw()), ImmPtr((void*)-1));
|
||||
}
|
||||
linkSelfReference(code);
|
||||
linkProfilerCallSites(code);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2436,29 +2411,41 @@ MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition* maybeDef, Reg
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::profilerPreCallImpl()
|
||||
MacroAssembler::AutoProfilerCallInstrumentation::AutoProfilerCallInstrumentation(
|
||||
MacroAssembler& masm
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
if (!masm.emitProfilingInstrumentation_)
|
||||
return;
|
||||
|
||||
Register reg = CallTempReg0;
|
||||
Register reg2 = CallTempReg1;
|
||||
push(reg);
|
||||
push(reg2);
|
||||
profilerPreCallImpl(reg, reg2);
|
||||
pop(reg2);
|
||||
pop(reg);
|
||||
}
|
||||
masm.push(reg);
|
||||
masm.push(reg2);
|
||||
|
||||
void
|
||||
MacroAssembler::profilerPreCallImpl(Register reg, Register reg2)
|
||||
{
|
||||
JitContext* icx = GetJitContext();
|
||||
AbsoluteAddress profilingActivation(icx->runtime->addressOfProfilingActivation());
|
||||
|
||||
CodeOffsetLabel label = movWithPatch(ImmWord(uintptr_t(-1)), reg);
|
||||
loadPtr(profilingActivation, reg2);
|
||||
storePtr(reg, Address(reg2, JitActivation::offsetOfLastProfilingCallSite()));
|
||||
CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), reg);
|
||||
masm.loadPtr(profilingActivation, reg2);
|
||||
masm.storePtr(reg, Address(reg2, JitActivation::offsetOfLastProfilingCallSite()));
|
||||
|
||||
appendProfilerCallSite(label);
|
||||
masm.appendProfilerCallSite(label);
|
||||
|
||||
masm.pop(reg2);
|
||||
masm.pop(reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::linkProfilerCallSites(JitCode* code)
|
||||
{
|
||||
for (size_t i = 0; i < profilerCallSites_.length(); i++) {
|
||||
CodeOffsetLabel offset = profilerCallSites_[i];
|
||||
offset.fixup(this);
|
||||
CodeLocationLabel location(code, offset);
|
||||
PatchDataWithValueCheck(location, ImmPtr(location.raw()), ImmPtr((void*)-1));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2546,11 +2533,11 @@ MacroAssembler::alignJitStackBasedOnNArgs(uint32_t nargs)
|
|||
|
||||
MacroAssembler::MacroAssembler(JSContext* cx, IonScript* ion,
|
||||
JSScript* script, jsbytecode* pc)
|
||||
: emitProfilingInstrumentation_(false),
|
||||
framePushed_(0)
|
||||
: framePushed_(0),
|
||||
#ifdef DEBUG
|
||||
, inCall_(false)
|
||||
inCall_(false),
|
||||
#endif
|
||||
emitProfilingInstrumentation_(false)
|
||||
{
|
||||
constructRoot(cx);
|
||||
jitContext_.emplace(cx, (js::jit::TempAllocator*)nullptr);
|
||||
|
@ -2566,7 +2553,7 @@ MacroAssembler::MacroAssembler(JSContext* cx, IonScript* ion,
|
|||
if (ion) {
|
||||
setFramePushed(ion->frameSize());
|
||||
if (pc && cx->runtime()->spsProfiler.enabled())
|
||||
emitProfilingInstrumentation_ = true;
|
||||
enableProfilingInstrumentation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2874,6 +2861,30 @@ MacroAssembler::callWithABINoProfiler(AsmJSImmPtr imm, MoveOp::Type result)
|
|||
callWithABIPost(stackAdjust, result);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Exit frame footer.
|
||||
|
||||
void
|
||||
MacroAssembler::linkExitFrame()
|
||||
{
|
||||
AbsoluteAddress jitTop(GetJitContext()->runtime->addressOfJitTop());
|
||||
storeStackPtr(jitTop);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::linkSelfReference(JitCode* code)
|
||||
{
|
||||
// If this code can transition to C++ code and witness a GC, then we need to store
|
||||
// the JitCode onto the stack in order to GC it correctly. exitCodePatch should
|
||||
// be unset if the code never needed to push its JitCode*.
|
||||
if (hasSelfReference()) {
|
||||
selfReferencePatch_.fixup(this);
|
||||
PatchDataWithValueCheck(CodeLocationLabel(code, selfReferencePatch_),
|
||||
ImmPtr(code),
|
||||
ImmPtr((void*)-1));
|
||||
}
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -172,6 +172,9 @@
|
|||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
// Defined in JitFrames.h
|
||||
enum ExitFrameTokenValues;
|
||||
|
||||
// The public entrypoint for emitting assembly. Note that a MacroAssembler can
|
||||
// use cx->lifoAlloc, so take care not to interleave masm use with other
|
||||
// lifoAlloc use if one will be destroyed before the other.
|
||||
|
@ -320,21 +323,16 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
mozilla::Maybe<AutoJitContextAlloc> alloc_;
|
||||
|
||||
private:
|
||||
// This field is used to manage profiling instrumentation output. If
|
||||
// provided and enabled, then instrumentation will be emitted around call
|
||||
// sites.
|
||||
bool emitProfilingInstrumentation_;
|
||||
|
||||
// Labels for handling exceptions and failures.
|
||||
NonAssertingLabel failureLabel_;
|
||||
|
||||
public:
|
||||
MacroAssembler()
|
||||
: emitProfilingInstrumentation_(false),
|
||||
framePushed_(0)
|
||||
: framePushed_(0),
|
||||
#ifdef DEBUG
|
||||
, inCall_(false)
|
||||
inCall_(false),
|
||||
#endif
|
||||
emitProfilingInstrumentation_(false)
|
||||
{
|
||||
JitContext* jcx = GetJitContext();
|
||||
JSContext* cx = jcx->cx;
|
||||
|
@ -365,11 +363,11 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// asm.js compilation handles its own JitContext-pushing
|
||||
struct AsmJSToken {};
|
||||
explicit MacroAssembler(AsmJSToken)
|
||||
: emitProfilingInstrumentation_(false),
|
||||
framePushed_(0)
|
||||
: framePushed_(0),
|
||||
#ifdef DEBUG
|
||||
, inCall_(false)
|
||||
inCall_(false),
|
||||
#endif
|
||||
emitProfilingInstrumentation_(false)
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
initWithAllocator();
|
||||
|
@ -380,10 +378,6 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
#endif
|
||||
}
|
||||
|
||||
void enableProfilingInstrumentation() {
|
||||
emitProfilingInstrumentation_ = true;
|
||||
}
|
||||
|
||||
void resetForNewCodeGenerator(TempAllocator& alloc);
|
||||
|
||||
void constructRoot(JSContext* cx) {
|
||||
|
@ -490,6 +484,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
inline void call(const CallSiteDesc& desc, const Register reg);
|
||||
inline void call(const CallSiteDesc& desc, Label* label);
|
||||
|
||||
// Push the return address and make a call. On platforms where this function
|
||||
// is not defined, push the link register (pushReturnAddress) at the entry
|
||||
// point of the callee.
|
||||
void callAndPushReturnAddress(Register reg) DEFINED_ON(mips32, x86_shared);
|
||||
void callAndPushReturnAddress(Label* label) DEFINED_ON(mips32, x86_shared);
|
||||
|
||||
void pushReturnAddress() DEFINED_ON(arm, arm64);
|
||||
|
||||
public:
|
||||
// ===============================================================
|
||||
// ABI function calls.
|
||||
|
@ -563,6 +565,96 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
uint32_t signature_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// ===============================================================
|
||||
// Jit Frames.
|
||||
//
|
||||
// These functions are used to build the content of the Jit frames. See
|
||||
// CommonFrameLayout class, and all its derivatives. The content should be
|
||||
// pushed in the opposite order as the fields of the structures, such that
|
||||
// the structures can be used to interpret the content of the stack.
|
||||
|
||||
// Call the Jit function, and push the return address (or let the callee
|
||||
// push the return address).
|
||||
//
|
||||
// These functions return the offset of the return address, in order to use
|
||||
// the return address to index the safepoints, which are used to list all
|
||||
// live registers.
|
||||
inline uint32_t callJitNoProfiler(Register callee);
|
||||
inline uint32_t callJit(Register callee);
|
||||
inline uint32_t callJit(JitCode* code);
|
||||
|
||||
// The frame descriptor is the second field of all Jit frames, pushed before
|
||||
// calling the Jit function. It is a composite value defined in JitFrames.h
|
||||
inline void makeFrameDescriptor(Register frameSizeReg, FrameType type);
|
||||
|
||||
// Push the frame descriptor, based on the statically known framePushed.
|
||||
inline void pushStaticFrameDescriptor(FrameType type);
|
||||
|
||||
// This function emulates a call by pushing an exit frame on the stack,
|
||||
// except that the fake-function is inlined within the body of the caller.
|
||||
//
|
||||
// This function assumes that the current frame is an IonJS frame.
|
||||
//
|
||||
// This function returns the offset of the /fake/ return address, in order to use
|
||||
// the return address to index the safepoints, which are used to list all
|
||||
// live registers.
|
||||
//
|
||||
// This function should be balanced with a call to adjustStack, to pop the
|
||||
// exit frame and emulate the return statement of the inlined function.
|
||||
inline uint32_t buildFakeExitFrame(Register scratch);
|
||||
|
||||
private:
|
||||
// This function is used by buildFakeExitFrame to push a fake return address
|
||||
// on the stack. This fake return address should never be used for resuming
|
||||
// any execution, and can even be an invalid pointer into the instruction
|
||||
// stream, as long as it does not alias any other.
|
||||
uint32_t pushFakeReturnAddress(Register scratch) PER_SHARED_ARCH;
|
||||
|
||||
public:
|
||||
// ===============================================================
|
||||
// Exit frame footer.
|
||||
//
|
||||
// When calling outside the Jit we push an exit frame. To mark the stack
|
||||
// correctly, we have to push additional information, called the Exit frame
|
||||
// footer, which is used to identify how the stack is marked.
|
||||
//
|
||||
// See JitFrames.h, and MarkJitExitFrame in JitFrames.cpp.
|
||||
|
||||
// If the current piece of code might be garbage collected, then the exit
|
||||
// frame footer must contain a pointer to the current JitCode, such that the
|
||||
// garbage collector can keep the code alive as long this code is on the
|
||||
// stack. This function pushes a placeholder which is replaced when the code
|
||||
// is linked.
|
||||
inline void PushStubCode();
|
||||
|
||||
// Return true if the code contains a self-reference which needs to be
|
||||
// patched when the code is linked.
|
||||
inline bool hasSelfReference() const;
|
||||
|
||||
// Push stub code and the VMFunction pointer.
|
||||
inline void enterExitFrame(const VMFunction* f = nullptr);
|
||||
|
||||
// Push an exit frame token to identify which fake exit frame this footer
|
||||
// corresponds to.
|
||||
inline void enterFakeExitFrame(enum ExitFrameTokenValues token);
|
||||
|
||||
// Pop ExitFrame footer in addition to the extra frame.
|
||||
inline void leaveExitFrame(size_t extraFrame = 0);
|
||||
|
||||
private:
|
||||
// Save the top of the stack into PerThreadData::jitTop of the main thread,
|
||||
// which should be the location of the latest exit frame.
|
||||
void linkExitFrame();
|
||||
|
||||
// Patch the value of PushStubCode with the pointer to the finalized code.
|
||||
void linkSelfReference(JitCode* code);
|
||||
|
||||
// If the JitCode that created this assembler needs to transition into the VM,
|
||||
// we want to store the JitCode on the stack in order to mark it during a GC.
|
||||
// This is a reference to a patch location where the JitCode* will be written.
|
||||
CodeOffsetLabel selfReferencePatch_;
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
public:
|
||||
|
||||
|
@ -1058,76 +1150,10 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
void compareStrings(JSOp op, Register left, Register right, Register result,
|
||||
Label* fail);
|
||||
|
||||
// If the JitCode that created this assembler needs to transition into the VM,
|
||||
// we want to store the JitCode on the stack in order to mark it during a GC.
|
||||
// This is a reference to a patch location where the JitCode* will be written.
|
||||
private:
|
||||
CodeOffsetLabel exitCodePatch_;
|
||||
|
||||
private:
|
||||
void linkExitFrame();
|
||||
|
||||
public:
|
||||
inline void PushStubCode();
|
||||
|
||||
// Push stub code, and the VMFunction pointer.
|
||||
inline void enterExitFrame(const VMFunction* f = nullptr);
|
||||
|
||||
// The JitCode * argument here is one of the tokens defined in the various
|
||||
// exit frame layout classes, e.g. NativeExitFrameLayout::Token().
|
||||
inline void enterFakeExitFrame(JitCode* codeVal);
|
||||
|
||||
// Pop ExitFrame footer in addition to the extra frame.
|
||||
inline void leaveExitFrame(size_t extraFrame = 0);
|
||||
|
||||
inline bool hasEnteredExitFrame() const;
|
||||
|
||||
// Generates code used to complete a bailout.
|
||||
void generateBailoutTail(Register scratch, Register bailoutInfo);
|
||||
|
||||
// These functions exist as small wrappers around sites where execution can
|
||||
// leave the currently running stream of instructions. They exist so that
|
||||
// instrumentation may be put in place around them if necessary and the
|
||||
// instrumentation is enabled. For the functions that return a uint32_t,
|
||||
// they are returning the offset of the assembler just after the call has
|
||||
// been made so that a safepoint can be made at that location.
|
||||
|
||||
// see above comment for what is returned
|
||||
uint32_t callJit(Register callee) {
|
||||
profilerPreCall();
|
||||
MacroAssemblerSpecific::callJit(callee);
|
||||
uint32_t ret = currentOffset();
|
||||
profilerPostReturn();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// see above comment for what is returned
|
||||
uint32_t callWithExitFrame(Label* target) {
|
||||
profilerPreCall();
|
||||
MacroAssemblerSpecific::callWithExitFrame(target);
|
||||
uint32_t ret = currentOffset();
|
||||
profilerPostReturn();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// see above comment for what is returned
|
||||
uint32_t callWithExitFrame(JitCode* target) {
|
||||
profilerPreCall();
|
||||
MacroAssemblerSpecific::callWithExitFrame(target);
|
||||
uint32_t ret = currentOffset();
|
||||
profilerPostReturn();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// see above comment for what is returned
|
||||
uint32_t callWithExitFrame(JitCode* target, Register dynStack) {
|
||||
profilerPreCall();
|
||||
MacroAssemblerSpecific::callWithExitFrame(target, dynStack);
|
||||
uint32_t ret = currentOffset();
|
||||
profilerPostReturn();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void branchTestObjectTruthy(bool truthy, Register objReg, Register scratch,
|
||||
Label* slowCheck, Label* checked)
|
||||
{
|
||||
|
@ -1218,22 +1244,41 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
}
|
||||
#endif // !JS_CODEGEN_ARM64
|
||||
|
||||
private:
|
||||
// These two functions are helpers used around call sites throughout the
|
||||
// assembler. They are called from the above call wrappers to emit the
|
||||
// necessary instrumentation.
|
||||
void profilerPreCall() {
|
||||
if (!emitProfilingInstrumentation_)
|
||||
return;
|
||||
profilerPreCallImpl();
|
||||
public:
|
||||
void enableProfilingInstrumentation() {
|
||||
emitProfilingInstrumentation_ = true;
|
||||
}
|
||||
|
||||
void profilerPostReturn() {
|
||||
if (!emitProfilingInstrumentation_)
|
||||
return;
|
||||
profilerPostReturnImpl();
|
||||
private:
|
||||
// This class is used to surround call sites throughout the assembler. This
|
||||
// is used by callWithABI, and callJit functions, except if suffixed by
|
||||
// NoProfiler.
|
||||
class AutoProfilerCallInstrumentation {
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
|
||||
|
||||
public:
|
||||
explicit AutoProfilerCallInstrumentation(MacroAssembler& masm
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
~AutoProfilerCallInstrumentation() {}
|
||||
};
|
||||
friend class AutoProfilerCallInstrumentation;
|
||||
|
||||
void appendProfilerCallSite(CodeOffsetLabel label) {
|
||||
propagateOOM(profilerCallSites_.append(label));
|
||||
}
|
||||
|
||||
// Fix up the code pointers to be written for locations where profilerCallSite
|
||||
// emitted moves of RIP to a register.
|
||||
void linkProfilerCallSites(JitCode* code);
|
||||
|
||||
// This field is used to manage profiling instrumentation output. If
|
||||
// provided and enabled, then instrumentation will be emitted around call
|
||||
// sites.
|
||||
bool emitProfilingInstrumentation_;
|
||||
|
||||
// Record locations of the call sites.
|
||||
Vector<CodeOffsetLabel, 0, SystemAllocPolicy> profilerCallSites_;
|
||||
|
||||
public:
|
||||
void loadBaselineOrIonRaw(Register script, Register dest, Label* failure);
|
||||
void loadBaselineOrIonNoArgCheck(Register callee, Register dest, Label* failure);
|
||||
|
@ -1545,10 +1590,6 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
bind(&ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
void profilerPreCallImpl();
|
||||
void profilerPreCallImpl(Register reg, Register reg2);
|
||||
void profilerPostReturnImpl() {}
|
||||
};
|
||||
|
||||
static inline Assembler::DoubleCondition
|
||||
|
|
|
@ -1865,31 +1865,6 @@ MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32
|
|||
return ma_vstr(src, Address(scratch, offset), cc);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::buildFakeExitFrame(Register scratch, uint32_t* offset)
|
||||
{
|
||||
DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
|
||||
uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
|
||||
|
||||
asMasm().Push(Imm32(descriptor)); // descriptor_
|
||||
|
||||
enterNoPool(2);
|
||||
DebugOnly<uint32_t> offsetBeforePush = currentOffset();
|
||||
asMasm().Push(pc); // actually pushes $pc + 8.
|
||||
|
||||
// Consume an additional 4 bytes. The start of the next instruction will
|
||||
// then be 8 bytes after the instruction for Push(pc); this offset can
|
||||
// therefore be fed to the safepoint.
|
||||
ma_nop();
|
||||
uint32_t pseudoReturnOffset = currentOffset();
|
||||
leaveNoPool();
|
||||
|
||||
MOZ_ASSERT(asMasm().framePushed() == initialDepth + ExitFrameLayout::Size());
|
||||
MOZ_ASSERT(pseudoReturnOffset - offsetBeforePush == 8);
|
||||
|
||||
*offset = pseudoReturnOffset;
|
||||
}
|
||||
|
||||
bool
|
||||
MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
|
||||
{
|
||||
|
@ -1902,59 +1877,6 @@ MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callWithExitFrame(Label* target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
|
||||
asMasm().Push(Imm32(descriptor)); // descriptor
|
||||
|
||||
ma_callJitHalfPush(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callWithExitFrame(JitCode* target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
|
||||
asMasm().Push(Imm32(descriptor)); // descriptor
|
||||
|
||||
addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
|
||||
RelocStyle rs;
|
||||
if (HasMOVWT())
|
||||
rs = L_MOVWT;
|
||||
else
|
||||
rs = L_LDR;
|
||||
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
ma_movPatchable(ImmPtr(target->raw()), scratch, Always, rs);
|
||||
ma_callJitHalfPush(scratch);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callWithExitFrame(JitCode* target, Register dynStack)
|
||||
{
|
||||
ma_add(Imm32(asMasm().framePushed()), dynStack);
|
||||
makeFrameDescriptor(dynStack, JitFrame_IonJS);
|
||||
asMasm().Push(dynStack); // descriptor
|
||||
|
||||
addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
|
||||
RelocStyle rs;
|
||||
if (HasMOVWT())
|
||||
rs = L_MOVWT;
|
||||
else
|
||||
rs = L_LDR;
|
||||
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
ma_movPatchable(ImmPtr(target->raw()), scratch, Always, rs);
|
||||
ma_callJitHalfPush(scratch);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callJit(Register callee)
|
||||
{
|
||||
MOZ_ASSERT((asMasm().framePushed() & 7) == 4);
|
||||
ma_callJitHalfPush(callee);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::alignFrameForICArguments(AfterICSaveLive& aic)
|
||||
{
|
||||
|
@ -3808,36 +3730,6 @@ MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest)
|
|||
ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
|
||||
}
|
||||
|
||||
// ARM says that all reads of pc will return 8 higher than the address of the
|
||||
// currently executing instruction. This means we are correctly storing the
|
||||
// address of the instruction after the call in the register.
|
||||
//
|
||||
// Also ION is breaking the ARM EABI here (sort of). The ARM EABI says that a
|
||||
// function call should move the pc into the link register, then branch to the
|
||||
// function, and *sp is data that is owned by the caller, not the callee. The
|
||||
// ION ABI says *sp should be the address that we will return to when leaving
|
||||
// this function.
|
||||
void
|
||||
MacroAssemblerARM::ma_callJitHalfPush(const Register r)
|
||||
{
|
||||
// The stack is unaligned by 4 bytes. We push the pc to the stack to align
|
||||
// the stack before the call, when we return the pc is poped and the stack
|
||||
// is restored to its unaligned state.
|
||||
as_blx(r);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARM::ma_callJitHalfPush(Label* label)
|
||||
{
|
||||
// The stack is unaligned by 4 bytes. The callee will push the lr to the stack to align
|
||||
// the stack after the call, when we return the pc is poped and the stack
|
||||
// is restored to its unaligned state.
|
||||
|
||||
// leave the stack as-is so the callee-side can push when necessary.
|
||||
|
||||
as_bl(label, Always);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARM::ma_call(ImmPtr dest)
|
||||
{
|
||||
|
@ -4964,14 +4856,6 @@ MacroAssemblerARMCompat::profilerExitFrame()
|
|||
branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callAndPushReturnAddress(Label* label)
|
||||
{
|
||||
AutoForbidPools afp(this, 2);
|
||||
ma_push(pc);
|
||||
asMasm().call(label);
|
||||
}
|
||||
|
||||
MacroAssembler&
|
||||
MacroAssemblerARM::asMasm()
|
||||
{
|
||||
|
@ -5186,9 +5070,14 @@ MacroAssembler::call(JitCode* c)
|
|||
|
||||
ScratchRegisterScope scratch(*this);
|
||||
ma_movPatchable(ImmPtr(c->raw()), scratch, Always, rs);
|
||||
ma_callJitHalfPush(scratch);
|
||||
callJitNoProfiler(scratch);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushReturnAddress()
|
||||
{
|
||||
push(lr);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// ABI function calls.
|
||||
|
@ -5307,4 +5196,28 @@ MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
|
|||
callWithABIPost(stackAdjust, result);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Jit Frames.
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::pushFakeReturnAddress(Register scratch)
|
||||
{
|
||||
// On ARM any references to the pc, adds an additional 8 to it, which
|
||||
// correspond to 2 instructions of 4 bytes. Thus we use an additional nop
|
||||
// to pad until we reach the pushed pc.
|
||||
//
|
||||
// Note: In practice this should not be necessary, as this fake return
|
||||
// address is never used for resuming any execution. Thus theoriticaly we
|
||||
// could just do a Push(pc), and ignore the nop as well as the pool.
|
||||
enterNoPool(2);
|
||||
DebugOnly<uint32_t> offsetBeforePush = currentOffset();
|
||||
Push(pc); // actually pushes $pc + 8.
|
||||
ma_nop();
|
||||
uint32_t pseudoReturnOffset = currentOffset();
|
||||
leaveNoPool();
|
||||
|
||||
MOZ_ASSERT(pseudoReturnOffset - offsetBeforePush == 8);
|
||||
return pseudoReturnOffset;
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
|
|
|
@ -429,13 +429,6 @@ class MacroAssemblerARM : public Assembler
|
|||
BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
|
||||
int32_t offset, Condition cc = Always);
|
||||
|
||||
// Calls an ion function, assuming that the stack is currently not 8 byte
|
||||
// aligned.
|
||||
void ma_callJitHalfPush(const Register reg);
|
||||
// Calls an ion function, assuming that the stack is currently not 8 byte
|
||||
// aligned.
|
||||
void ma_callJitHalfPush(Label* label);
|
||||
|
||||
void ma_call(ImmPtr dest);
|
||||
|
||||
// Float registers can only be loaded/stored in continuous runs when using
|
||||
|
@ -532,8 +525,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
MOZ_CRASH("NYI-IC");
|
||||
}
|
||||
|
||||
void callAndPushReturnAddress(Label* label);
|
||||
|
||||
void branch(JitCode* c) {
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
|
||||
|
@ -1188,30 +1179,12 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
void storeTypeTag(ImmTag tag, const Address& dest);
|
||||
void storeTypeTag(ImmTag tag, const BaseIndex& dest);
|
||||
|
||||
void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
|
||||
ma_lsl(Imm32(FRAMESIZE_SHIFT), frameSizeReg, frameSizeReg);
|
||||
ma_orr(Imm32(type), frameSizeReg);
|
||||
}
|
||||
|
||||
void handleFailureWithHandlerTail(void* handler);
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Common interface.
|
||||
/////////////////////////////////////////////////////////////////
|
||||
public:
|
||||
// Builds an exit frame on the stack, with a return address to an internal
|
||||
// non-function. Returns offset to be passed to markSafepointAt().
|
||||
void buildFakeExitFrame(Register scratch, uint32_t* offset);
|
||||
|
||||
void callWithExitFrame(Label* target);
|
||||
void callWithExitFrame(JitCode* target);
|
||||
void callWithExitFrame(JitCode* target, Register dynStack);
|
||||
|
||||
// Makes a call using the only two methods that it is sane for
|
||||
// independent code to make a call.
|
||||
void callJit(Register callee);
|
||||
void callJitFromAsmJS(Register callee) { as_blx(callee); }
|
||||
|
||||
void add32(Register src, Register dest);
|
||||
void add32(Imm32 imm, Register dest);
|
||||
void add32(Imm32 imm, const Address& dest);
|
||||
|
@ -1818,10 +1791,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
void loadAsmJSHeapRegisterFromGlobalData() {
|
||||
loadPtr(Address(GlobalReg, AsmJSHeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg);
|
||||
}
|
||||
void pushReturnAddress() {
|
||||
push(lr);
|
||||
}
|
||||
|
||||
// Instrumentation for entering and leaving the profiler.
|
||||
void profilerEnterFrame(Register framePtr, Register scratch);
|
||||
void profilerExitFrame();
|
||||
|
|
|
@ -287,7 +287,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
|||
masm.push(scratch);
|
||||
masm.push(Imm32(0)); // Fake return address.
|
||||
// No GC things to mark on the stack, push a bare token.
|
||||
masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
|
||||
masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
|
||||
|
||||
masm.push(framePtr); // BaselineFrame
|
||||
masm.push(r0); // jitcode
|
||||
|
@ -348,7 +348,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
|||
masm.assertStackAlignment(JitStackAlignment, sizeof(uintptr_t));
|
||||
|
||||
// Call the function.
|
||||
masm.ma_callJitHalfPush(r0);
|
||||
masm.callJitNoProfiler(r0);
|
||||
|
||||
if (type == EnterJitBaseline) {
|
||||
// Baseline OSR will return here.
|
||||
|
@ -540,9 +540,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
|
|||
masm.andPtr(Imm32(CalleeTokenMask), r1);
|
||||
masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3);
|
||||
masm.loadBaselineOrIonRaw(r3, r3, nullptr);
|
||||
masm.ma_callJitHalfPush(r3);
|
||||
|
||||
uint32_t returnOffset = masm.currentOffset();
|
||||
uint32_t returnOffset = masm.callJitNoProfiler(r3);
|
||||
|
||||
// arg1
|
||||
// ...
|
||||
|
|
|
@ -36,52 +36,6 @@ MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
|
|||
Csel(dest, dest, wzr, GreaterThan);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::buildFakeExitFrame(Register scratch, uint32_t* offset)
|
||||
{
|
||||
mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
|
||||
uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
|
||||
|
||||
asMasm().Push(Imm32(descriptor)); // descriptor_
|
||||
|
||||
enterNoPool(3);
|
||||
Label fakeCallsite;
|
||||
Adr(ARMRegister(scratch, 64), &fakeCallsite);
|
||||
asMasm().Push(scratch);
|
||||
bind(&fakeCallsite);
|
||||
uint32_t pseudoReturnOffset = currentOffset();
|
||||
leaveNoPool();
|
||||
|
||||
MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
|
||||
|
||||
*offset = pseudoReturnOffset;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::callWithExitFrame(Label* target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
|
||||
Push(Imm32(descriptor)); // descriptor
|
||||
asMasm().call(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::callWithExitFrame(JitCode* target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
|
||||
asMasm().Push(Imm32(descriptor));
|
||||
asMasm().call(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::callWithExitFrame(JitCode* target, Register dynStack)
|
||||
{
|
||||
add32(Imm32(framePushed()), dynStack);
|
||||
makeFrameDescriptor(dynStack, JitFrame_IonJS);
|
||||
Push(dynStack); // descriptor
|
||||
asMasm().call(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::alignFrameForICArguments(MacroAssembler::AfterICSaveLive& aic)
|
||||
{
|
||||
|
@ -287,25 +241,6 @@ MacroAssemblerCompat::branchValueIsNurseryObject(Condition cond, ValueOperand va
|
|||
temp, ImmWord(nursery.nurserySize()), label);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::callAndPushReturnAddress(Label* label)
|
||||
{
|
||||
// FIXME: Jandem said he would refactor the code to avoid making
|
||||
// this instruction required, but probably forgot about it.
|
||||
// Instead of implementing this function, we should make it unnecessary.
|
||||
Label ret;
|
||||
{
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch64 = temps.AcquireX();
|
||||
|
||||
Adr(scratch64, &ret);
|
||||
asMasm().Push(scratch64.asUnsized());
|
||||
}
|
||||
|
||||
Bl(label);
|
||||
bind(&ret);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::breakpoint()
|
||||
{
|
||||
|
@ -515,6 +450,12 @@ MacroAssembler::call(JitCode* c)
|
|||
blr(scratch64);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushReturnAddress()
|
||||
{
|
||||
push(lr);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// ABI function calls.
|
||||
|
||||
|
@ -625,6 +566,24 @@ MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
|
|||
callWithABIPost(stackAdjust, result);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Jit Frames.
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::pushFakeReturnAddress(Register scratch)
|
||||
{
|
||||
enterNoPool(3);
|
||||
Label fakeCallsite;
|
||||
|
||||
Adr(ARMRegister(scratch, 64), &fakeCallsite);
|
||||
Push(scratch);
|
||||
bind(&fakeCallsite);
|
||||
uint32_t pseudoReturnOffset = currentOffset();
|
||||
|
||||
leaveNoPool();
|
||||
return = pseudoReturnOffset;
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -207,9 +207,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
vixl::MacroAssembler::Pop(r0, r1, r2, r3);
|
||||
}
|
||||
|
||||
void pushReturnAddress() {
|
||||
push(lr);
|
||||
}
|
||||
void pop(const ValueOperand& v) {
|
||||
pop(v.valueReg());
|
||||
}
|
||||
|
@ -2640,12 +2637,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
|
||||
void handleFailureWithHandlerTail(void* handler);
|
||||
|
||||
// FIXME: This is the same on all platforms. Can be common code?
|
||||
void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
|
||||
lshiftPtr(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
|
||||
orPtr(Imm32(type), frameSizeReg);
|
||||
}
|
||||
|
||||
// FIXME: See CodeGeneratorX64 calls to noteAsmJSGlobalAccess.
|
||||
void patchAsmJSGlobalAccess(CodeOffsetLabel patchAt, uint8_t* code,
|
||||
uint8_t* globalData, unsigned globalDataOffset)
|
||||
|
@ -2665,20 +2656,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
|
||||
void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
|
||||
|
||||
// Builds an exit frame on the stack, with a return address to an internal
|
||||
// non-function. Returns offset to be passed to markSafepointAt().
|
||||
void buildFakeExitFrame(Register scratch, uint32_t* offset);
|
||||
|
||||
void callWithExitFrame(Label* target);
|
||||
void callWithExitFrame(JitCode* target);
|
||||
void callWithExitFrame(JitCode* target, Register dynStack);
|
||||
|
||||
void callJit(Register callee) {
|
||||
// AArch64 cannot read from the PC, so pushing must be handled callee-side.
|
||||
syncStackPtr();
|
||||
Blr(ARMRegister(callee, 64));
|
||||
}
|
||||
|
||||
void appendCallSite(const CallSiteDesc& desc) {
|
||||
MOZ_CRASH("appendCallSite");
|
||||
}
|
||||
|
@ -2687,12 +2664,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
MOZ_CRASH("callExit");
|
||||
}
|
||||
|
||||
void callJitFromAsmJS(Register reg) {
|
||||
Blr(ARMRegister(reg, 64));
|
||||
}
|
||||
|
||||
void callAndPushReturnAddress(Label* label);
|
||||
|
||||
void profilerEnterFrame(Register framePtr, Register scratch) {
|
||||
AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
|
||||
loadPtr(activation, scratch);
|
||||
|
|
|
@ -189,7 +189,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
|||
masm.makeFrameDescriptor(r19, JitFrame_BaselineJS);
|
||||
masm.asVIXL().Push(x19, xzr); // Push xzr for a fake return address.
|
||||
// No GC things to mark: push a bare token.
|
||||
masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
|
||||
masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
|
||||
|
||||
masm.push(BaselineFrameReg, reg_code);
|
||||
|
||||
|
@ -225,7 +225,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
|||
|
||||
// Call function.
|
||||
// Since AArch64 doesn't have the pc register available, the callee must push lr.
|
||||
masm.call(reg_code);
|
||||
masm.callJitNoProfiler(reg_code);
|
||||
|
||||
// Baseline OSR will return here.
|
||||
if (type == EnterJitBaseline)
|
||||
|
@ -405,8 +405,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
|
|||
// Load the address of the code that is getting called.
|
||||
masm.Ldr(x3, MemOperand(x5, JSFunction::offsetOfNativeOrScript()));
|
||||
masm.loadBaselineOrIonRaw(r3, r3, nullptr);
|
||||
masm.call(r3);
|
||||
uint32_t returnOffset = masm.currentOffset();
|
||||
uint32_t returnOffset = masm.callJitNoProfiler(r3);
|
||||
|
||||
// Clean up!
|
||||
// Get the size of the stack frame, and clean up the later fixed frame.
|
||||
|
|
|
@ -1498,25 +1498,6 @@ MacroAssemblerMIPS::ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label* label,
|
|||
branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::buildFakeExitFrame(Register scratch, uint32_t* offset)
|
||||
{
|
||||
mozilla::DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
|
||||
|
||||
CodeLabel cl;
|
||||
ma_li(scratch, cl.dest());
|
||||
|
||||
uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
|
||||
asMasm().Push(Imm32(descriptor));
|
||||
asMasm().Push(scratch);
|
||||
|
||||
bind(cl.src());
|
||||
*offset = currentOffset();
|
||||
|
||||
MOZ_ASSERT(asMasm().framePushed() == initialDepth + ExitFrameLayout::Size());
|
||||
addCodeLabel(cl);
|
||||
}
|
||||
|
||||
bool
|
||||
MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
|
||||
{
|
||||
|
@ -1528,45 +1509,6 @@ MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::callWithExitFrame(Label* target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
|
||||
asMasm().Push(Imm32(descriptor)); // descriptor
|
||||
|
||||
ma_callJitHalfPush(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::callWithExitFrame(JitCode* target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
|
||||
asMasm().Push(Imm32(descriptor)); // descriptor
|
||||
|
||||
addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
|
||||
ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
|
||||
ma_callJitHalfPush(ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::callWithExitFrame(JitCode* target, Register dynStack)
|
||||
{
|
||||
ma_addu(dynStack, dynStack, Imm32(asMasm().framePushed()));
|
||||
makeFrameDescriptor(dynStack, JitFrame_IonJS);
|
||||
asMasm().Push(dynStack); // descriptor
|
||||
|
||||
addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
|
||||
ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
|
||||
ma_callJitHalfPush(ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::callJit(Register callee)
|
||||
{
|
||||
MOZ_ASSERT((asMasm().framePushed() & 7) == 4);
|
||||
ma_callJitHalfPush(callee);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::add32(Register src, Register dest)
|
||||
{
|
||||
|
@ -3063,28 +3005,6 @@ MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest)
|
|||
as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET);
|
||||
}
|
||||
|
||||
// This macrosintruction calls the ion code and pushes the return address to
|
||||
// the stack in the case when stack is not alligned.
|
||||
void
|
||||
MacroAssemblerMIPS::ma_callJitHalfPush(const Register r)
|
||||
{
|
||||
// This is a MIPS hack to push return address during jalr delay slot.
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
|
||||
as_jalr(r);
|
||||
as_sw(ra, StackPointer, 0);
|
||||
}
|
||||
|
||||
// This macrosintruction calls the ion code and pushes the return address to
|
||||
// the stack in the case when stack is not alligned.
|
||||
void
|
||||
MacroAssemblerMIPS::ma_callJitHalfPush(Label* label)
|
||||
{
|
||||
// This is a MIPS hack to push return address during jalr delay slot.
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
|
||||
ma_bal(label, DontFillDelaySlot);
|
||||
as_sw(ra, StackPointer, 0);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_call(ImmPtr dest)
|
||||
{
|
||||
|
@ -3518,7 +3438,25 @@ MacroAssembler::call(JitCode* c)
|
|||
BufferOffset bo = m_buffer.nextOffset();
|
||||
addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
|
||||
ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
|
||||
ma_callJitHalfPush(ScratchRegister);
|
||||
callJitNoProfiler(ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callAndPushReturnAddress(Register callee)
|
||||
{
|
||||
// Push return address during jalr delay slot.
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
|
||||
as_jalr(callee);
|
||||
as_sw(ra, StackPointer, 0);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callAndPushReturnAddress(Label* label)
|
||||
{
|
||||
// Push return address during jalr delay slot.
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
|
||||
as_jalr(label);
|
||||
as_sw(ra, StackPointer, 0);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -3622,4 +3560,21 @@ MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
|
|||
callWithABIPost(stackAdjust, result);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Jit Frames.
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::pushFakeReturnAddress(Register scratch)
|
||||
{
|
||||
CodeLabel cl;
|
||||
|
||||
ma_li(scratch, cl.dest());
|
||||
Push(scratch);
|
||||
bind(cl.src());
|
||||
uint32_t retAddr = currentOffset();
|
||||
|
||||
addCodeLabel(cl);
|
||||
return retAddr;
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
|
|
|
@ -315,10 +315,6 @@ class MacroAssemblerMIPS : public Assembler
|
|||
FPConditionBit fcc = FCC0);
|
||||
|
||||
public:
|
||||
// calls an ion function, assuming that the stack is currently not 8 byte aligned
|
||||
void ma_callJitHalfPush(const Register reg);
|
||||
void ma_callJitHalfPush(Label* label);
|
||||
|
||||
void ma_call(ImmPtr dest);
|
||||
|
||||
void ma_jump(ImmPtr dest);
|
||||
|
@ -375,10 +371,6 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
|||
MOZ_CRASH("NYI-IC");
|
||||
}
|
||||
|
||||
void callAndPushReturnAddress(Label* label) {
|
||||
ma_callJitHalfPush(label);
|
||||
}
|
||||
|
||||
void branch(JitCode* c) {
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
|
||||
|
@ -890,11 +882,6 @@ public:
|
|||
void storeTypeTag(ImmTag tag, Address dest);
|
||||
void storeTypeTag(ImmTag tag, const BaseIndex& dest);
|
||||
|
||||
void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
|
||||
ma_sll(frameSizeReg, frameSizeReg, Imm32(FRAMESIZE_SHIFT));
|
||||
ma_or(frameSizeReg, frameSizeReg, Imm32(type));
|
||||
}
|
||||
|
||||
void handleFailureWithHandlerTail(void* handler);
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
@ -1117,19 +1104,6 @@ public:
|
|||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
// Builds an exit frame on the stack, with a return address to an internal
|
||||
// non-function. Returns offset to be passed to markSafepointAt().
|
||||
void buildFakeExitFrame(Register scratch, uint32_t* offset);
|
||||
|
||||
void callWithExitFrame(Label* target);
|
||||
void callWithExitFrame(JitCode* target);
|
||||
void callWithExitFrame(JitCode* target, Register dynStack);
|
||||
|
||||
// Makes a call using the only two methods that it is sane for indep code
|
||||
// to make a call.
|
||||
void callJit(Register callee);
|
||||
void callJitFromAsmJS(Register callee) { callJit(callee); }
|
||||
|
||||
void add32(Register src, Register dest);
|
||||
void add32(Imm32 imm, Register dest);
|
||||
void add32(Imm32 imm, const Address& dest);
|
||||
|
|
|
@ -241,7 +241,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
|||
masm.storePtr(zero, Address(StackPointer, 0)); // fake return address
|
||||
|
||||
// No GC things to mark, push a bare token.
|
||||
masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
|
||||
masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
|
||||
|
||||
masm.reserveStack(2 * sizeof(uintptr_t));
|
||||
masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
|
||||
|
@ -303,7 +303,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
|||
masm.assertStackAlignment(JitStackAlignment, sizeof(uintptr_t));
|
||||
|
||||
// Call the function with pushing return address to stack.
|
||||
masm.ma_callJitHalfPush(reg_code);
|
||||
masm.callJitNoProfiler(reg_code);
|
||||
|
||||
if (type == EnterJitBaseline) {
|
||||
// Baseline OSR will return here.
|
||||
|
@ -497,9 +497,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
|
|||
masm.andPtr(Imm32(CalleeTokenMask), calleeTokenReg);
|
||||
masm.loadPtr(Address(calleeTokenReg, JSFunction::offsetOfNativeOrScript()), t1);
|
||||
masm.loadBaselineOrIonRaw(t1, t1, nullptr);
|
||||
masm.ma_callJitHalfPush(t1);
|
||||
|
||||
uint32_t returnOffset = masm.currentOffset();
|
||||
uint32_t returnOffset = masm.callJitNoProfiler(t1);
|
||||
|
||||
// arg1
|
||||
// ...
|
||||
|
|
|
@ -193,17 +193,6 @@ class MacroAssemblerNone : public Assembler
|
|||
uint32_t labelOffsetToPatchOffset(uint32_t) { MOZ_CRASH(); }
|
||||
CodeOffsetLabel labelForPatch() { MOZ_CRASH(); }
|
||||
|
||||
template <typename T> void call(T) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void call(T, S) { MOZ_CRASH(); }
|
||||
void callAndPushReturnAddress(Label* label) { MOZ_CRASH(); }
|
||||
|
||||
void callWithExitFrame(Label*) { MOZ_CRASH(); }
|
||||
void callWithExitFrame(JitCode*) { MOZ_CRASH(); }
|
||||
void callWithExitFrame(JitCode*, Register) { MOZ_CRASH(); }
|
||||
|
||||
void callJit(Register callee) { MOZ_CRASH(); }
|
||||
void callJitFromAsmJS(Register callee) { MOZ_CRASH(); }
|
||||
|
||||
void nop() { MOZ_CRASH(); }
|
||||
void breakpoint() { MOZ_CRASH(); }
|
||||
void abiret() { MOZ_CRASH(); }
|
||||
|
@ -449,7 +438,6 @@ class MacroAssemblerNone : public Assembler
|
|||
void incrementInt32Value(Address) { MOZ_CRASH(); }
|
||||
void ensureDouble(ValueOperand, FloatRegister, Label*) { MOZ_CRASH(); }
|
||||
void handleFailureWithHandlerTail(void*) { MOZ_CRASH(); }
|
||||
void makeFrameDescriptor(Register, FrameType) { MOZ_CRASH(); }
|
||||
|
||||
void branchPtrInNurseryRange(Condition, Register, Register, Label*) { MOZ_CRASH(); }
|
||||
void branchValueIsNurseryObject(Condition, ValueOperand, Register, Label*) { MOZ_CRASH(); }
|
||||
|
|
|
@ -914,7 +914,6 @@ class AssemblerShared
|
|||
Vector<AsmJSAbsoluteLink, 0, SystemAllocPolicy> asmJSAbsoluteLinks_;
|
||||
|
||||
protected:
|
||||
Vector<CodeOffsetLabel, 0, SystemAllocPolicy> profilerCallSites_;
|
||||
bool enoughMemory_;
|
||||
bool embedsNurseryPointers_;
|
||||
|
||||
|
@ -936,10 +935,6 @@ class AssemblerShared
|
|||
return !enoughMemory_;
|
||||
}
|
||||
|
||||
void appendProfilerCallSite(CodeOffsetLabel label) {
|
||||
enoughMemory_ &= profilerCallSites_.append(label);
|
||||
}
|
||||
|
||||
bool embedsNurseryPointers() const {
|
||||
return embedsNurseryPointers_;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче