Merge m-c to fx-team, a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2016-07-08 15:04:54 -07:00
Родитель 9a353f444c 2a800a6763
Коммит 4725525760
146 изменённых файлов: 2405 добавлений и 1892 удалений

Просмотреть файл

@ -12,6 +12,8 @@
#include "Logging.h"
#endif
#include "mozilla/UniquePtr.h"
using namespace mozilla;
using namespace mozilla::a11y;
@ -162,7 +164,7 @@ EventTree::Process(const RefPtr<DocAccessible>& aDeathGrip)
return;
}
}
mFirst = mFirst->mNext.forget();
mFirst = Move(mFirst->mNext);
}
MOZ_ASSERT(mContainer || mDependentEvents.IsEmpty(),
@ -229,11 +231,12 @@ EventTree*
EventTree::FindOrInsert(Accessible* aContainer)
{
if (!mFirst) {
return mFirst = new EventTree(aContainer, true);
mFirst.reset(new EventTree(aContainer, true));
return mFirst.get();
}
EventTree* prevNode = nullptr;
EventTree* node = mFirst;
EventTree* node = mFirst.get();
do {
MOZ_ASSERT(!node->mContainer->IsApplication(),
"No event for application accessible is expected here");
@ -277,18 +280,18 @@ EventTree::FindOrInsert(Accessible* aContainer)
// Insert the tail node into the hierarchy between the current node and
// its parent.
node->mFireReorder = false;
nsAutoPtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
nsAutoPtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
newNode->mFirst = Move(nodeOwnerRef);
nodeOwnerRef = Move(newNode);
nodeOwnerRef->mNext = Move(node->mNext);
// Check if a next node is contained by the given node too, and move them
// under the given node if so.
prevNode = nodeOwnerRef;
node = nodeOwnerRef->mNext;
nsAutoPtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
EventTree* insNode = nodeOwnerRef->mFirst;
prevNode = nodeOwnerRef.get();
node = nodeOwnerRef->mNext.get();
UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
EventTree* insNode = nodeOwnerRef->mFirst.get();
while (node) {
Accessible* curParent = node->mContainer;
while (curParent && !curParent->IsDoc()) {
@ -301,7 +304,7 @@ EventTree::FindOrInsert(Accessible* aContainer)
node->mFireReorder = false;
insNode->mNext = Move(*nodeRef);
insNode = insNode->mNext;
insNode = insNode->mNext.get();
prevNode->mNext = Move(node->mNext);
node = prevNode;
@ -310,14 +313,14 @@ EventTree::FindOrInsert(Accessible* aContainer)
prevNode = node;
nodeRef = &node->mNext;
node = node->mNext;
node = node->mNext.get();
}
return nodeOwnerRef;
return nodeOwnerRef.get();
}
prevNode = node;
} while ((node = node->mNext));
} while ((node = node->mNext.get()));
MOZ_ASSERT(prevNode, "Nowhere to insert");
MOZ_ASSERT(!prevNode->mNext, "Taken by another node");
@ -327,7 +330,8 @@ EventTree::FindOrInsert(Accessible* aContainer)
// if a dependent show event target contains the given container then do not
// emit show / hide events (see Process() method)
return prevNode->mNext = new EventTree(aContainer, mDependentEvents.IsEmpty());
prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
return prevNode->mNext.get();
}
void
@ -357,14 +361,14 @@ EventTree::Find(const Accessible* aContainer) const
}
if (et->mFirst) {
et = et->mFirst;
et = et->mFirst.get();
const EventTree* cet = et->Find(aContainer);
if (cet) {
return cet;
}
}
et = et->mNext;
et = et->mNext.get();
const EventTree* cet = et->Find(aContainer);
if (cet) {
return cet;
@ -421,7 +425,7 @@ EventTree::Mutated(AccMutationEvent* aEv)
{
// If shown or hidden node is a root of previously mutated subtree, then
// discard those subtree mutations as we are no longer interested in them.
nsAutoPtr<EventTree>* node = &mFirst;
UniquePtr<EventTree>* node = &mFirst;
while (*node) {
if ((*node)->mContainer == aEv->mAccessible) {
*node = Move((*node)->mNext);

Просмотреть файл

@ -10,6 +10,7 @@
#include "Accessible.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace a11y {
@ -101,8 +102,8 @@ private:
void Mutated(AccMutationEvent* aEv);
void Clear();
nsAutoPtr<EventTree> mFirst;
nsAutoPtr<EventTree> mNext;
UniquePtr<EventTree> mFirst;
UniquePtr<EventTree> mNext;
Accessible* mContainer;
nsTArray<RefPtr<AccMutationEvent>> mDependentEvents;

Просмотреть файл

@ -2211,7 +2211,7 @@ Accessible::EmbeddedChildCount()
{
if (mStateFlags & eHasTextKids) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
return mEmbeddedObjCollector->Count();
}
@ -2223,8 +2223,8 @@ Accessible::GetEmbeddedChildAt(uint32_t aIndex)
{
if (mStateFlags & eHasTextKids) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
return mEmbeddedObjCollector.get() ?
mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nullptr;
}
@ -2236,8 +2236,8 @@ Accessible::GetIndexOfEmbeddedChild(Accessible* aChild)
{
if (mStateFlags & eHasTextKids) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
return mEmbeddedObjCollector.get() ?
mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
}

Просмотреть файл

@ -11,7 +11,8 @@
#include "mozilla/a11y/Role.h"
#include "mozilla/a11y/States.h"
#include "nsAutoPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsIContent.h"
#include "nsString.h"
#include "nsTArray.h"
@ -1138,7 +1139,7 @@ protected:
friend class xpcAccessible;
friend class TreeMutation;
nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
UniquePtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
union {
int32_t mIndexOfEmbeddedChild;
uint32_t mProxyInterfaces;

Просмотреть файл

@ -396,7 +396,7 @@ exports.testLocalURLwithInvalidURL = function(assert) {
exports.testFileName = function(assert) {
let urls = [
['https://foo/bar.js', 'bar.js'],
['app://myfxosapp/file.js', 'file.js'],
['chrome://gaia/content/myfxosapp/file.js', 'file.js'],
['http://localhost:8888/file.js', 'file.js'],
['http://foo/bar.js#hash', 'bar.js'],
['http://foo/bar.js?q=go&query=yeah', 'bar.js'],

Просмотреть файл

@ -276,6 +276,20 @@ const PREF_SSL_IMPACT = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
}, []);
function getSerializedSecurityInfo(docShell) {
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo;
if (!securityInfo) {
return "";
}
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
.QueryInterface(Ci.nsISerializable);
return serhelper.serializeToString(securityInfo);
}
var AboutNetAndCertErrorListener = {
init: function(chromeGlobal) {
addMessageListener("CertErrorDetails", this);
@ -417,19 +431,10 @@ var AboutNetAndCertErrorListener = {
// if we're enabling reports, send a report for this failure
if (evt.detail) {
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
let serializable = docShell.failedChannel.securityInfo
.QueryInterface(Ci.nsITransportSecurityInfo)
.QueryInterface(Ci.nsISerializable);
let serializedSecurityInfo = serhelper.serializeToString(serializable);
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
sendAsyncMessage("Browser:SendSSLErrorReport", {
uri: { host, port },
securityInfo: serializedSecurityInfo
securityInfo: getSerializedSecurityInfo(docShell),
});
}
@ -542,26 +547,14 @@ var ClickEventHandler = {
},
onCertError: function (targetElement, ownerDoc) {
let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
let serializedSecurityInfo = "";
try {
let serializable = docShell.failedChannel.securityInfo
.QueryInterface(Ci.nsITransportSecurityInfo)
.QueryInterface(Ci.nsISerializable);
serializedSecurityInfo = serhelper.serializeToString(serializable);
} catch (e) { }
sendAsyncMessage("Browser:CertExceptionError", {
location: ownerDoc.location.href,
elementId: targetElement.getAttribute("id"),
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
securityInfoAsString: serializedSecurityInfo
securityInfoAsString: getSerializedSecurityInfo(docShell),
});
},

Просмотреть файл

@ -4,11 +4,13 @@ support-files =
empty_file.html
file_reflect_cookie_into_title.html
favicon-normal32.png
file_set_storages.html
serviceworker.html
worker.js
[browser_aboutURLs.js]
[browser_favicon.js]
[browser_forgetaboutsite.js]
[browser_usercontext.js]
[browser_usercontextid_tabdrop.js]
skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276

Просмотреть файл

@ -0,0 +1,351 @@
/*
* Bug 1238183 - Test cases for forgetAboutSite with userContextId.
*/
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/ForgetAboutSite.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
let LoadContextInfo = Cc["@mozilla.org/load-context-info-factory;1"]
.getService(Ci.nsILoadContextInfoFactory);
let css = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
.getService(Ci.nsICacheStorageService);
const USER_CONTEXTS = [
"default",
"personal",
"work",
];
const TEST_HOST = "example.com";
const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
const COOKIE_NAME = "userContextId";
// Counter for image load hits.
let gHits = 0;
let gHttpServer = null;
function imageHandler(metadata, response) {
// A 1x1 PNG image.
// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
"ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
gHits++;
response.setHeader("Cache-Control", "max-age=10000", false);
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "image/png", false);
response.write(IMAGE);
}
function loadImagePageHandler(metadata, response) {
response.setHeader("Cache-Control", "max-age=10000", false);
response.setStatusLine(metadata.httpVersion, 200, "Ok");
response.setHeader("Content-Type", "text/html", false);
let body = "<!DOCTYPE HTML>\
<html>\
<head>\
<meta charset='utf-8'>\
<title>Load Image</title>\
</head>\
<body>\
<img src='image.png'>\
</body>\
</html>";
response.bodyOutputStream.write(body, body.length);
}
function* openTabInUserContext(uri, userContextId) {
// Open the tab in the correct userContextId.
let tab = gBrowser.addTab(uri, {userContextId});
// Select tab and make sure its browser is focused.
gBrowser.selectedTab = tab;
tab.ownerDocument.defaultView.focus();
let browser = gBrowser.getBrowserForTab(tab);
yield BrowserTestUtils.browserLoaded(browser);
return {tab, browser};
}
function getCookiesForOA(host, userContextId) {
return Services.cookies.getCookiesFromHost(host, {userContextId});
}
function createURI(uri)
{
let ioServ = Cc["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
return ioServ.newURI(uri, null, null);
}
function getCacheStorage(where, lci, appcache)
{
if (!lci) lci = LoadContextInfo.default;
switch (where) {
case "disk": return css.diskCacheStorage(lci, false);
case "memory": return css.memoryCacheStorage(lci);
case "appcache": return css.appCacheStorage(lci, appcache);
case "pin": return css.pinningCacheStorage(lci);
}
return null;
}
function OpenCacheEntry(key, where, flags, lci)
{
return new Promise(resolve => {
key = createURI(key);
function CacheListener() { }
CacheListener.prototype = {
_appCache: null,
QueryInterface: function (iid) {
if (iid.equals(Components.interfaces.nsICacheEntryOpenCallback) ||
iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
onCacheEntryCheck: function(entry, appCache) {
return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
},
onCacheEntryAvailable: function (entry, isnew, appCache, status) {
resolve();
},
run: function () {
let storage = getCacheStorage(where, lci, this._appCache);
storage.asyncOpenURI(key, "", flags, this);
}
};
(new CacheListener()).run();
});
}
//
// Test functions.
//
// Cookies
function* test_cookie_cleared() {
let tabs = [];
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// Load the page in 3 different contexts and set a cookie
// which should only be visible in that context.
let value = USER_CONTEXTS[userContextId];
// Open our tab in the given user context.
tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "file_reflect_cookie_into_title.html?" + value, userContextId);
// Close this tab.
yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
}
// Check that cookies have been set properly.
for (let userContextId of Object.keys(USER_CONTEXTS)) {
let enumerator = getCookiesForOA(TEST_HOST, userContextId);
ok(enumerator.hasMoreElements(), "Cookies available");
let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
Assert.equal(foundCookie["name"], COOKIE_NAME, "Check cookie name");
Assert.equal(foundCookie["value"], USER_CONTEXTS[userContextId], "Check cookie value");
}
// Forget the site.
ForgetAboutSite.removeDataFromDomain(TEST_HOST);
// Check that whether cookies has been cleared or not.
for (let userContextId of Object.keys(USER_CONTEXTS)) {
let enumerator = getCookiesForOA(TEST_HOST, userContextId);
ok(!enumerator.hasMoreElements(), "No Cookie should be here");
}
}
// Cache
function* test_cache_cleared() {
// First, add some caches.
for (let userContextId of Object.keys(USER_CONTEXTS)) {
yield OpenCacheEntry("http://" + TEST_HOST + "/",
"disk",
Ci.nsICacheStorage.OPEN_NORMALLY,
LoadContextInfo.custom(false, false, {userContextId}));
yield OpenCacheEntry("http://" + TEST_HOST + "/",
"memory",
Ci.nsICacheStorage.OPEN_NORMALLY,
LoadContextInfo.custom(false, false, {userContextId}));
}
// Check that caches have been set correctly.
for (let userContextId of Object.keys(USER_CONTEXTS)) {
let mem = getCacheStorage("memory");
let disk = getCacheStorage("disk");
Assert.ok(mem.exists(createURI("http://" + TEST_HOST + "/"), ""), "The memory cache has been set correctly");
Assert.ok(disk.exists(createURI("http://" + TEST_HOST + "/"), ""), "The disk cache has been set correctly");
}
// Forget the site.
ForgetAboutSite.removeDataFromDomain(TEST_HOST);
// Check that do caches be removed or not?
for (let userContextId of Object.keys(USER_CONTEXTS)) {
let mem = getCacheStorage("memory");
let disk = getCacheStorage("disk");
Assert.ok(!mem.exists(createURI("http://" + TEST_HOST + "/"), ""), "The memory cache is cleared");
Assert.ok(!disk.exists(createURI("http://" + TEST_HOST + "/"), ""), "The disk cache is cleared");
}
}
// Image Cache
function* test_image_cache_cleared() {
let tabs = [];
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// Open our tab in the given user context to cache image.
tabs[userContextId] = yield* openTabInUserContext('http://localhost:' + gHttpServer.identity.primaryPort + '/loadImage.html',
userContextId);
yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
}
// Check that image cache works with the userContextId.
todo_is(gHits, 3, "The image should be loaded three times. This test should be enabled after the bug 1270680 landed");
// Reset the cache count.
gHits = 0;
// Forget the site.
ForgetAboutSite.removeDataFromDomain("localhost:" + gHttpServer.identity.primaryPort + "/");
// Load again.
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// Open our tab in the given user context to cache image.
tabs[userContextId] = yield* openTabInUserContext('http://localhost:' + gHttpServer.identity.primaryPort + '/loadImage.html',
userContextId);
yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
}
// Check that image cache was cleared and the server gets another three hits.
todo_is(gHits, 3, "The image should be loaded three times. This test should be enabled after the bug 1270680 landed");
}
// Offline Storage
function* test_storage_cleared() {
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// Load the page in 3 different contexts and set the local storage
// which should only be visible in that context.
let value = USER_CONTEXTS[userContextId];
// Open our tab in the given user context.
let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html?" + value, userContextId);
// Check that the local storage has been set correctly.
let win = tabInfo.browser.contentWindow;
Assert.equal(win.localStorage.getItem("userContext"), USER_CONTEXTS[userContextId], "Check the local storage value");
// Check that the session storage has been set correctly.
Assert.equal(win.sessionStorage.getItem("userContext"), USER_CONTEXTS[userContextId], "Check the session storage value");
// Check that the indexedDB has been set correctly.
yield ContentTask.spawn(tabInfo.browser, { userContext: USER_CONTEXTS[userContextId] }, function* (arg) {
let request = content.indexedDB.open("idb", 1);
let db = yield new Promise(done => {
request.onsuccess = event => {
done(event.target.result);
};
});
let transaction = db.transaction(["obj"], "readonly");
let store = transaction.objectStore("obj");
let storeRequest = store.get(1);
yield new Promise(done => {
storeRequest.onsuccess = event => {
let res = storeRequest.result;
Assert.equal(res.userContext, arg.userContext, "Check the indexedDB value");
done();
};
});
});
// Close this tab.
yield BrowserTestUtils.removeTab(tabInfo.tab);
}
// Forget the site.
ForgetAboutSite.removeDataFromDomain(TEST_HOST);
// Open the tab again without setting the localStorage and check that the
// local storage has been cleared or not.
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// Open our tab in the given user context without setting local storage.
let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html", userContextId);
let win = tabInfo.browser.contentWindow;
// Check that does the local storage be cleared or not.
Assert.ok(!win.localStorage.getItem("userContext"), "The local storage has been cleared");
// Check that does the session storage be cleared or not.
Assert.ok(!win.sessionStorage.getItem("userContext"), "The session storage has been cleared");
// Check that does the indexedDB be cleared or not.
yield ContentTask.spawn(tabInfo.browser, null, function* () {
let request = content.indexedDB.open("idb", 1);
let db = yield new Promise(done => {
request.onsuccess = event => {
done(event.target.result);
};
});
try {
let transaction = db.transaction(["obj"], "readonly");
Assert.ok(false, "The indexedDB should not exist");
} catch (e) {
Assert.equal(e.name, "NotFoundError", "The indexedDB does not exist as expected");
}
});
// Close the tab.
yield BrowserTestUtils.removeTab(tabInfo.tab);
}
}
add_task(function* setup() {
// Make sure userContext is enabled.
yield new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["privacy.userContext.enabled", true]
]}, resolve);
});
// Create a http server for the image cache test.
if (!gHttpServer) {
gHttpServer = new HttpServer();
gHttpServer.registerPathHandler('/image.png', imageHandler);
gHttpServer.registerPathHandler('/loadImage.html', loadImagePageHandler);
gHttpServer.start(-1);
}
});
let tests = [
test_cookie_cleared,
test_cache_cleared,
test_image_cache_cleared,
test_storage_cleared,
];
add_task(function* test() {
for (let i = 0; i < tests.length; i++)
add_task(tests[i]);
});
registerCleanupFunction(() => {
gHttpServer.stop(() => {
gHttpServer = null;
});
});

Просмотреть файл

@ -0,0 +1,41 @@
<html>
<head>
<meta charset="UTF-8">
<title>Bug 1238183</title>
</head>
<body>
<script type="application/javascript;version=1.7">
"use strict";
// if we have a query string, use it to set storages
if (window.location.search.length > 0) {
let context_name = window.location.search.substr(1);
localStorage.setItem("userContext", context_name);
sessionStorage.setItem("userContext", context_name);
let request = indexedDB.open("idb", 1);
request.onerror = function() {
throw new Error("error opening db connection");
};
request.onupgradeneeded = event => {
let db = event.target.result;
let store = db.createObjectStore("obj", { keyPath: "id" });
store.createIndex("userContext", "userContext", { unique: false });
};
request.onsuccess = event => {
let db = request.result;
let transaction = db.transaction(["obj"], "readwrite");
let store = transaction.objectStore("obj");
store.add({id: 1, userContext: context_name});
transaction.oncomplete = () => {
db.close();
};
};
}
</script>
</body>
</html>

Просмотреть файл

@ -13,4 +13,8 @@ export MOZ_TELEMETRY_REPORTING=1
ac_add_options --disable-stdcxx-compat
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
# Bug 1283438
mk_add_options AUTOCLOBBER=
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -13,4 +13,8 @@ export MOZ_TELEMETRY_REPORTING=1
ac_add_options --disable-stdcxx-compat
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
# Bug 1283438
mk_add_options AUTOCLOBBER=
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -14,5 +14,9 @@ export MOZILLA_OFFICIAL=1
# Enable Telemetry
export MOZ_TELEMETRY_REPORTING=1
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
# Bug 1283438
mk_add_options AUTOCLOBBER=
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

Просмотреть файл

@ -4,5 +4,9 @@ ac_add_options --with-l10n-base=../../l10n
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --with-branding=browser/branding/nightly
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
# Bug 1283438
mk_add_options AUTOCLOBBER=
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

Просмотреть файл

@ -10,6 +10,10 @@ export MOZILLA_OFFICIAL=1
# Enable Telemetry
export MOZ_TELEMETRY_REPORTING=1
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
# Bug 1283438
mk_add_options AUTOCLOBBER=
. $topsrcdir/build/win32/mozconfig.vs-latest
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -11,6 +11,10 @@ export MOZILLA_OFFICIAL=1
# Enable Telemetry
export MOZ_TELEMETRY_REPORTING=1
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
# Bug 1283438
mk_add_options AUTOCLOBBER=
. $topsrcdir/build/win64/mozconfig.vs-latest
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -115,6 +115,8 @@ def mozconfig(current_project, mozconfig, old_configure, build_env, help):
return mozconfig
set_config('MOZCONFIG', depends(mozconfig)(lambda m: m['path']))
# Hacks related to old-configure
# ==============================

Просмотреть файл

@ -7,8 +7,6 @@
# Avoid duplication if the file happens to be included twice.
if test -z "$bucket" -a -z "$NO_CACHE"; then
# buildbot (or builders that use buildprops.json):
if [ -f $topsrcdir/../buildprops.json ]; then
read branch platform master <<EOF
$(python2.7 -c 'import json; p = json.loads(open("'"$topsrcdir"'/../buildprops.json").read())["properties"]; print p["branch"], p["platform"], p["master"]' 2> /dev/null)
EOF
@ -42,46 +40,6 @@ if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET" -a -z
esac
fi
# taskcluster:
else
# timeout after 1 second, and don't retry (failure indicates instance is not in ec2 or wget, network issue)
# availability_zone is of the form <region><letter> where region is e.g. us-west-2, and az is us-west-2a
availability_zone=$(wget -T 1 -t 1 -q -O - http://169.254.169.254/latest/meta-data/placement/availability-zone)
# region is az with last letter trimmed
region=${availability_zone%?}
if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET" -a -z "$MOZ_PGO"; then
# set S3 bucket according to tree (level)
case "${GECKO_HEAD_REPOSITORY}" in
*hg.mozilla.org/try*)
bucket=taskcluster-level-1-sccache-${region}
;;
*hg.mozilla.org/integration/mozilla-inbound*|*hg.mozilla.org/integration/fx-team*)
bucket=taskcluster-level-3-sccache-${region}
;;
esac
fi
# set a dummy master
case "${region}" in
us-east-1)
master=dummy.use1.mozilla.com
;;
us-west-1)
master=dummy.usw1.mozilla.com
;;
us-west-2)
master=dummy.usw2.mozilla.com
;;
esac
# set platform based on the SYSTEMROOT env var
case "${SYSTEMROOT}" in
*Windows)
platform=windows
;;
esac
fi
if test -z "$bucket"; then
case "$platform" in
win*) : ;;
@ -95,7 +53,7 @@ else
fi
mk_add_options "export SCCACHE_BUCKET=$bucket"
case "$master" in
*us[ew][12].mozilla.com*)
*use1.mozilla.com*|*usw2.mozilla.com*)
mk_add_options "export SCCACHE_NAMESERVER=169.254.169.253"
;;
esac

Просмотреть файл

@ -50,6 +50,7 @@ def config_status(config):
sanitized_config['non_global_defines'] = config['non_global_defines']
sanitized_config['topsrcdir'] = config['TOPSRCDIR']
sanitized_config['topobjdir'] = config['TOPOBJDIR']
sanitized_config['mozconfig'] = config.get('MOZCONFIG')
# Create config.status. Eventually, we'll want to just do the work it does
# here, when we're able to skip configure tests/use cached results/not rely
@ -68,7 +69,7 @@ def config_status(config):
json.dump(v, fh, sort_keys=True, indent=4, ensure_ascii=False)
fh.write('\n')
fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', "
"'non_global_defines', 'substs']")
"'non_global_defines', 'substs', 'mozconfig']")
if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'):
fh.write('''

Просмотреть файл

@ -14,7 +14,6 @@ const CONTENT_LOCATIONS = [
"hello/<.world (http://foo/bar.js?myquery=params&search=1:123:987)",
"hello/<.world (http://foo/#bar:123:987)",
"hello/<.world (http://foo/:123:987)",
"hello/<.world (app://myfxosapp/file.js:100:1)",
// Test scripts with port numbers (bug 1164131)
"hello/<.world (http://localhost:8888/file.js:100:1)",
@ -72,7 +71,6 @@ add_task(function () {
["hello/<.world", "bar.js", "foo", "http://foo/bar.js?myquery=params&search=1", 123, 987, "foo", null],
["hello/<.world", "/", "foo", "http://foo/#bar", 123, 987, "foo", null],
["hello/<.world", "/", "foo", "http://foo/", 123, 987, "foo", null],
["hello/<.world", "file.js", "myfxosapp", "app://myfxosapp/file.js", 100, 1, "myfxosapp", null],
["hello/<.world", "file.js", "localhost:8888", "http://localhost:8888/file.js", 100, 1, "localhost:8888", 8888],
["hello/<.world", "file.js", "localhost:8888", "http://localhost:8888/file.js", 100, null, "localhost:8888", 8888],
["hello/<.world", "file.js (eval:1)", "localhost:8888", "http://localhost:8888/file.js", 65, null, "localhost:8888", 8888],

Просмотреть файл

@ -16,8 +16,6 @@ TEST_HARNESS_FILES.testing.mochitest.tests.devtools.shared.apps.tests.data += [
'tests/data/app.zip',
]
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
DevToolsModules(
'app-actor-front.js',
'Devices.jsm',

Просмотреть файл

@ -1,4 +0,0 @@
{
// Extend from the common devtools xpcshell eslintrc config.
"extends": "../../../../.eslintrc.xpcshell"
}

Двоичный файл не отображается.

Двоичные данные
devtools/shared/apps/tests/unit/data/app.zip

Двоичный файл не отображается.

Просмотреть файл

@ -1,123 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
const {DebuggerClient} = require("devtools/shared/client/main");
const {DebuggerServer} = require("devtools/server/main");
const {AppActorFront} = require("devtools/shared/apps/app-actor-front");
var gClient, gActor, gActorFront;
function connect(onDone) {
// Initialize a loopback remote protocol connection
DebuggerServer.init();
// We need to register browser actors to have `listTabs` working
// and also have a root actor
DebuggerServer.addBrowserActors();
// Setup client and actor used in all tests
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect()
.then(() => gClient.listTabs())
.then(aResponse => {
gActor = aResponse.webappsActor;
gActorFront = new AppActorFront(gClient, aResponse);
onDone();
});
}
function webappActorRequest(request, onResponse) {
if (!gActor) {
connect(webappActorRequest.bind(null, request, onResponse));
return;
}
request.to = gActor;
gClient.request(request, onResponse);
}
// Install a test packaged webapp from data folder
function installTestApp(zipName, appId, onDone) {
// Copy our package to tmp folder, where the actor retrieves it
let zip = do_get_file("data/" + zipName);
let appDir = FileUtils.getDir("TmpD", ["b2g", appId], true, true);
zip.copyTo(appDir, "application.zip");
let request = {type: "install", appId: appId};
webappActorRequest(request, function (aResponse) {
do_check_eq(aResponse.appId, appId);
if ("error" in aResponse) {
do_throw("Error: " + aResponse.error);
}
if ("message" in aResponse) {
do_throw("Error message: " + aResponse.message);
}
do_check_false("error" in aResponse);
onDone();
});
}
function setup() {
// We have to setup a profile, otherwise indexed db used by webapps
// will throw random exception when trying to get profile folder
do_get_profile();
// The webapps dir isn't registered on b2g xpcshell tests,
// we have to manually set it to the directory service.
do_get_webappsdir();
// We also need a valid nsIXulAppInfo service as Webapps.jsm is querying it
Components.utils.import("resource://testing-common/AppInfo.jsm");
updateAppInfo();
Components.utils.import("resource://gre/modules/Webapps.jsm");
// Enable launch/close method of the webapps actor
let {WebappsActor} = require("devtools/server/actors/webapps");
WebappsActor.prototype.supportsLaunch = true;
}
function do_get_webappsdir() {
var webappsDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
webappsDir.append("test_webapps");
if (!webappsDir.exists())
webappsDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("755", 8));
var coreAppsDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
coreAppsDir.append("test_coreapps");
if (!coreAppsDir.exists())
coreAppsDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("755", 8));
// Register our own provider for the profile directory.
// It will return our special docshell profile directory.
var provider = {
getFile: function (prop, persistent) {
persistent.value = true;
if (prop == "webappsDir") {
return webappsDir.clone();
}
else if (prop == "coreAppsDir") {
return coreAppsDir.clone();
}
throw Cr.NS_ERROR_FAILURE;
},
QueryInterface: function (iid) {
if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
iid.equals(Ci.nsISupports)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
}
};
Services.dirsvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
}

Просмотреть файл

@ -1,6 +0,0 @@
if (gClient) {
// Close the test remote connection before leaving this test
gClient.close(function () {
run_next_test();
});
}

Просмотреть файл

@ -1,311 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const promise = require("promise");
const defer = require("devtools/shared/defer");
var gAppId = "actor-test";
const APP_ORIGIN = "app://" + gAppId;
add_test(function testLaunchInexistantApp() {
let request = {type: "launch", manifestURL: "http://foo.com"};
webappActorRequest(request, function (aResponse) {
do_check_eq(aResponse.error, "NO_SUCH_APP");
run_next_test();
});
});
add_test(function testCloseInexistantApp() {
let request = {type: "close", manifestURL: "http://foo.com"};
webappActorRequest(request, function (aResponse) {
do_check_eq(aResponse.error, "missingParameter");
do_check_eq(aResponse.message, "No application for http://foo.com");
run_next_test();
});
});
// Install a test app
add_test(function testInstallPackaged() {
installTestApp("app.zip", gAppId, function () {
run_next_test();
});
});
// Now check that the app appear in getAll
add_test(function testGetAll() {
let request = {type: "getAll"};
webappActorRequest(request, function (aResponse) {
do_check_true("apps" in aResponse);
let apps = aResponse.apps;
do_check_true(apps.length > 0);
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
if (app.id == gAppId) {
do_check_eq(app.name, "Test app");
do_check_eq(app.manifest.description, "Testing webapps actor");
do_check_eq(app.manifest.launch_path, "/index.html");
do_check_eq(app.origin, APP_ORIGIN);
do_check_eq(app.installOrigin, app.origin);
do_check_eq(app.manifestURL, app.origin + "/manifest.webapp");
do_check_eq(app.csp, "script-src: http://foo.com");
run_next_test();
return;
}
}
do_throw("Unable to find the test app by its id");
});
});
add_test(function testGetApp() {
let manifestURL = APP_ORIGIN + "/manifest.webapp";
let request = {type: "getApp", manifestURL: manifestURL};
webappActorRequest(request, function (aResponse) {
do_check_true("app" in aResponse);
let app = aResponse.app;
do_check_eq(app.id, gAppId);
do_check_eq(app.name, "Test app");
do_check_eq(app.manifest.description, "Testing webapps actor");
do_check_eq(app.manifest.launch_path, "/index.html");
do_check_eq(app.origin, APP_ORIGIN);
do_check_eq(app.installOrigin, app.origin);
do_check_eq(app.manifestURL, app.origin + "/manifest.webapp");
run_next_test();
});
});
add_test(function testLaunchApp() {
let manifestURL = APP_ORIGIN + "/manifest.webapp";
let startPoint = "/index.html";
let request = {
type: "launch",
manifestURL: manifestURL,
startPoint: startPoint
};
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let json = JSON.parse(data);
do_check_eq(json.manifestURL, manifestURL);
do_check_eq(json.startPoint, startPoint);
run_next_test();
}, "webapps-launch", false);
webappActorRequest(request, function (aResponse) {
do_check_false("error" in aResponse);
});
});
add_test(function testCloseApp() {
let manifestURL = APP_ORIGIN + "/manifest.webapp";
let request = {
type: "close",
manifestURL: manifestURL
};
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let json = JSON.parse(data);
do_check_eq(json.manifestURL, manifestURL);
}, "webapps-close", false);
webappActorRequest(request, function (aResponse) {
do_check_false("error" in aResponse);
run_next_test();
});
});
// The 128px icon is a single red pixel and the 64px one is a blue one
// bug 899177: there is a bug with xhr and app:// and jar:// uris
// that ends up forcing the content type to application/xml
var red1px = "data:application/xml;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4z8AAAAMBAQAY3Y2wAAAAAElFTkSuQmCC";
var blue1px = "data:application/xml;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12MwZDgHAAFlAQBDpjhLAAAAAElFTkSuQmCC";
add_test(function testGetIcon() {
let manifestURL = APP_ORIGIN + "/manifest.webapp";
let request = {
type: "getIconAsDataURL",
manifestURL: manifestURL
};
webappActorRequest(request, function (aResponse) {
do_check_false("error" in aResponse);
// By default, getIconAsDataURL return the 128x128 icon
do_check_eq(aResponse.url, red1px);
run_next_test();
});
});
add_test(function testGetIconWithCustomSize() {
let manifestURL = APP_ORIGIN + "/manifest.webapp";
let request = {
type: "getIconAsDataURL",
manifestURL: manifestURL,
size: 64
};
webappActorRequest(request, function (aResponse) {
do_check_false("error" in aResponse);
do_check_eq(aResponse.url, blue1px);
run_next_test();
});
});
add_test(function testUninstall() {
let manifestURL = APP_ORIGIN + "/manifest.webapp";
let request = {
type: "uninstall",
manifestURL: manifestURL
};
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let json = JSON.parse(data);
do_check_eq(json.manifestURL, manifestURL);
do_check_eq(json.origin, APP_ORIGIN);
do_check_eq(json.id, gAppId);
run_next_test();
}, "webapps-uninstall", false);
webappActorRequest(request, function (aResponse) {
do_check_false("error" in aResponse);
});
});
add_test(function testFileUploadInstall() {
let packageFile = do_get_file("data/app.zip");
// Disable the bulk trait temporarily to test the JSON upload path
gClient.traits.bulk = false;
let progressDeferred = defer();
// Ensure we get at least one progress event at the end
gActorFront.on("install-progress", function onProgress(e, progress) {
if (progress.bytesSent == progress.totalBytes) {
gActorFront.off("install-progress", onProgress);
progressDeferred.resolve();
}
});
let installed =
gActorFront.installPackaged(packageFile.path, gAppId)
.then(function ({ appId }) {
do_check_eq(appId, gAppId);
}, function (e) {
do_throw("Failed install uploaded packaged app: " + e.error + ": " + e.message);
});
promise.all([progressDeferred.promise, installed])
.then(() => {
// Restore default bulk trait value
gClient.traits.bulk = true;
run_next_test();
});
});
add_test(function testBulkUploadInstall() {
let packageFile = do_get_file("data/app.zip");
do_check_true(gClient.traits.bulk);
let progressDeferred = defer();
// Ensure we get at least one progress event at the end
gActorFront.on("install-progress", function onProgress(e, progress) {
if (progress.bytesSent == progress.totalBytes) {
gActorFront.off("install-progress", onProgress);
progressDeferred.resolve();
}
});
let installed =
gActorFront.installPackaged(packageFile.path, gAppId)
.then(function ({ appId }) {
do_check_eq(appId, gAppId);
}, function (e) {
do_throw("Failed bulk install uploaded packaged app: " + e.error + ": " + e.message);
});
promise.all([progressDeferred.promise, installed])
.then(run_next_test);
});
add_test(function testInstallHosted() {
gAppId = "hosted-app";
let metadata = {
origin: "http://foo.com",
installOrigin: "http://metadata.foo.com",
manifestURL: "http://foo.com/metadata/manifest.webapp"
};
let manifest = {
name: "My hosted app",
csp: "script-src: http://foo.com"
};
gActorFront.installHosted(gAppId, metadata, manifest)
.then(function ({ appId }) {
do_check_eq(appId, gAppId);
run_next_test();
},
function (e) {
do_throw("Failed installing hosted app: " + e.error + ": " + e.message);
});
});
add_test(function testCheckHostedApp() {
let request = {type: "getAll"};
webappActorRequest(request, function (aResponse) {
do_check_true("apps" in aResponse);
let apps = aResponse.apps;
do_check_true(apps.length > 0);
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
if (app.id == gAppId) {
do_check_eq(app.name, "My hosted app");
do_check_eq(app.origin, "http://foo.com");
do_check_eq(app.installOrigin, "http://metadata.foo.com");
do_check_eq(app.manifestURL, "http://foo.com/metadata/manifest.webapp");
do_check_eq(app.csp, "script-src: http://foo.com");
run_next_test();
return;
}
}
do_throw("Unable to find the test app by its id");
});
});
add_test(function testInstallOverrideSystem() {
let appId = "actor-test"; // Match app.zip id
// Make the test app non-removable, like system apps
DOMApplicationRegistry.webapps[appId].removable = false;
let packageFile = do_get_file("data/app.zip");
gActorFront.installPackaged(packageFile.path, appId)
.then(function ({ appId }) {
do_throw("Override of a non-removable app has been accepted.");
}, function (e) {
do_check_eq(e.message, "The application " + appId + " can't be overridden.");
// Then make it removable again...
DOMApplicationRegistry.webapps[appId].removable = true;
run_next_test();
});
});
add_test(function testInstallEngineeringMode() {
let appId = "actor-test"; // Match app.zip id
let packageFile = do_get_file("data/app-engineering.zip");
gActorFront.installPackaged(packageFile.path, appId)
.then(function ({ appId }) {
do_throw("App with engineering mode permission was accepted.");
}, function (e) {
do_check_eq(e.message, "Installing apps with any of these permissions is forbidden: " +
Services.prefs.getCharPref("devtools.apps.forbidden-permissions"));
run_next_test();
});
});
function run_test() {
setup();
run_next_test();
}

Просмотреть файл

@ -1,12 +0,0 @@
[DEFAULT]
tags = devtools
head = head_apps.js
tail = tail_apps.js
firefox-appdir = browser
skip-if = toolkit == 'android' || (toolkit == 'gonk' && debug) # Bug 1206586
support-files =
data/app.zip
data/app-engineering.zip
[test_webappsActor.js]
skip-if = buildapp == "b2g"

Просмотреть файл

@ -1405,7 +1405,7 @@ KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
// what we need for animating backface-visibility correctly if we
// remove the above test for Extend3DContext(); that would require
// looking at backface-visibility on descendants as well. See bug 1186204.
if (aFrame->StyleDisplay()->BackfaceIsHidden()) {
if (aFrame->BackfaceIsHidden()) {
aPerformanceWarning =
AnimationPerformanceWarning::Type::TransformBackfaceVisibilityHidden;
return false;

Просмотреть файл

@ -447,6 +447,28 @@ promise_test(function(t) {
});
}, 'animation is removed from the compositor when setting null target');
promise_test(function(t) {
var div = addDiv(t);
var animation = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC,
delay: 100 * MS_PER_SEC,
fill: 'backwards' });
return animation.ready.then(function() {
// Will be fixed in bug 1223658.
assert_equals(animation.isRunningOnCompositor, false,
'Animation with fill:backwards in delay phase reports ' +
'that it is NOT running on the compositor');
animation.currentTime = 100 * MS_PER_SEC;
return waitForFrame();
}).then(function() {
assert_equals(animation.isRunningOnCompositor, omtaEnabled,
'Animation with fill:backwards in delay phase reports ' +
'that it is running on the compositor after delay phase');
});
}, 'animation with fill:backwards in delay phase is running on the ' +
' main-thread while it is in delay phase');
</script>
</body>

Просмотреть файл

@ -7,4 +7,3 @@ support-files =
[test_basic.html]
[test_webkitdirectory.html]
[test_worker_basic.html]
skip-if = true # bug 1283344

Просмотреть файл

@ -8,17 +8,20 @@
</head>
<body>
<input id="fileList" type="file"></input>
<script type="application/javascript;version=1.7">
var directory;
var fileList;
function create_fileList(aPath) {
fileList = document.createElement('input');
fileList.setAttribute('type', 'file');
document.body.appendChild(fileList);
var url = SimpleTest.getTestFileURL("script_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
fileList.getFilesAndDirectories().then(function(array) {
@ -40,7 +43,6 @@ function test_simpleFilePicker(aPath) {
var script = SpecialPowers.loadChromeScript(url);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
is(fileList.files.length, 1, "we want 1 element");
@ -61,7 +63,6 @@ function test_duplicateGetFilesAndDirectories() {
var script = SpecialPowers.loadChromeScript(url);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
var p1 = fileList.getFilesAndDirectories();
@ -82,7 +83,6 @@ function test_inputGetFiles() {
var script = SpecialPowers.loadChromeScript(url);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
fileList.getFilesAndDirectories()

Просмотреть файл

@ -8,15 +8,19 @@
</head>
<body>
<input id="fileList" type="file"></input>
<script type="application/javascript;version=1.7">
var fileList;
function create_fileList() {
fileList = document.createElement('input');
fileList.setAttribute('type', 'file');
document.body.appendChild(fileList);
var url = SimpleTest.getTestFileURL("script_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
script.destroy();
next();
@ -27,7 +31,6 @@ function create_fileList() {
}
function test_worker() {
var fileList = document.getElementById('fileList');
fileList.getFilesAndDirectories().then(function(array) {
var worker = new Worker('worker_basic.js');
worker.onmessage = function(e) {

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -46,10 +46,6 @@ function testSteps()
// This one lives in storage/permanent/chrome
{ dbName: "dbL", dbVersion: 1 },
// This one lives in storage/default/1007+f+app+++system.gaiamobile.org
{ appId: 1007, inIsolatedMozBrowser: false, url: "app://system.gaiamobile.org",
dbName: "dbM", dbVersion: 1 },
// This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
{ appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
dbName: "dbN", dbVersion: 1 },
@ -91,10 +87,6 @@ function testSteps()
{ url: "http://localhost:82", dbName: "dbW",
dbOptions: { version: 1, storage: "temporary" } },
// This one lives in storage/temporary/1007+f+app+++system.gaiamobile.org
{ appId: 1007, inIsolatedMozBrowser: false, url: "app://system.gaiamobile.org",
dbName: "dbX", dbOptions: { version: 1, storage: "temporary" } },
// This one lives in storage/temporary/1007+t+https+++developer.cdn.mozilla.net
{ appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
dbName: "dbY", dbOptions: { version: 1, storage: "temporary" } },

Просмотреть файл

@ -11,10 +11,6 @@ function testSteps()
// This one lives in storage/default/http+++www.mozilla.org
{ url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
// This one lives in storage/default/1007+f+app+++system.gaiamobile.org
{ appId: 1007, inIsolatedMozBrowser: false, url: "app://system.gaiamobile.org",
dbName: "dbM", dbVersion: 1 },
// This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
{ appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
dbName: "dbN", dbVersion: 1 },

Просмотреть файл

@ -11,10 +11,6 @@ function testSteps()
// This one lives in storage/default/http+++www.mozilla.org
{ url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
// This one lives in storage/default/1007+f+app+++system.gaiamobile.org
{ appId: 1007, inIsolatedMozBrowser: false, url: "app://system.gaiamobile.org",
dbName: "dbM", dbVersion: 1 },
// This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
{ appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
dbName: "dbN", dbVersion: 1 },

Просмотреть файл

@ -11,10 +11,6 @@ function testSteps()
// This one lives in storage/default/http+++www.mozilla.org
{ url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
// This one lives in storage/default/1007+f+app+++system.gaiamobile.org
{ appId: 1007, inIsolatedMozBrowser: false, url: "app://system.gaiamobile.org",
dbName: "dbM", dbVersion: 1 },
// This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
{ appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
dbName: "dbN", dbVersion: 1 },

Просмотреть файл

@ -45,7 +45,7 @@ AccurateSeekTask::AccurateSeekTask(const void* aDecoderID,
: SeekTask(aDecoderID, aThread, aReader, Move(aSeekJob))
, mCurrentTimeBeforeSeek(media::TimeUnit::FromMicroseconds(aCurrentMediaTime))
, mAudioRate(aInfo.mAudio.mRate)
, mDoneAudioSeeking(!aInfo.HasAudio() || aSeekJob.mTarget.IsVideoOnly())
, mDoneAudioSeeking(!aInfo.HasAudio() || mSeekJob.mTarget.IsVideoOnly())
, mDoneVideoSeeking(!aInfo.HasVideo())
{
AssertOwnerThread();
@ -309,6 +309,14 @@ AccurateSeekTask::OnAudioDecoded(MediaData* aAudioSample)
SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
audio->mTime, audio->GetEndTime(), audio->mDiscontinuity);
// Video-only seek doesn't reset audio decoder. There might be pending audio
// requests when AccurateSeekTask::Seek() begins. We will just store the data
// without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget().
if (mSeekJob.mTarget.IsVideoOnly()) {
mSeekedAudioData = audio.forget();
return;
}
if (mFirstAudioSample) {
mFirstAudioSample = false;
MOZ_ASSERT(audio->mDiscontinuity);
@ -341,6 +349,11 @@ AccurateSeekTask::OnNotDecoded(MediaData::Type aType,
SAMPLE_LOG("OnNotDecoded type=%d reason=%u", aType, aReason);
// Ignore pending requests from video-only seek.
if (aType == MediaData::AUDIO_DATA && mSeekJob.mTarget.IsVideoOnly()) {
return;
}
if (aReason == MediaDecoderReader::DECODE_ERROR) {
// If this is a decode error, delegate to the generic error path.
RejectIfExist(__func__);
@ -446,6 +459,10 @@ AccurateSeekTask::SetCallbacks()
mAudioWaitCallback = mReader->AudioWaitCallback().Connect(
OwnerThread(), [this] (WaitCallbackData aData) {
// Ignore pending requests from video-only seek.
if (mSeekJob.mTarget.IsVideoOnly()) {
return;
}
if (aData.is<MediaData::Type>()) {
RequestAudioData();
}

Просмотреть файл

@ -1,6 +1,7 @@
[DEFAULT]
head = head.js head-http2.js
tail =
firefox-appdir = browser
# Push notifications and alarms are currently disabled on Android.
skip-if = toolkit == 'android'

Просмотреть файл

@ -0,0 +1 @@
var foo = 24;

Просмотреть файл

@ -0,0 +1,54 @@
// custom *.sjs for Bug 1277557
// META CSP: require-sri-for script;
const PRE_INTEGRITY =
"<!DOCTYPE HTML>" +
"<html><head><meta charset=\"utf-8\">" +
"<title>Bug 1277557 - CSP require-sri-for does not block when CSP is in meta tag</title>" +
"<meta http-equiv=\"Content-Security-Policy\" content=\"require-sri-for script; script-src 'unsafe-inline' *\">" +
"</head>" +
"<body>" +
"<script id=\"testscript\"" +
// Using math.random() to avoid confusing cache behaviors within the test
" src=\"http://mochi.test:8888/tests/dom/security/test/csp/file_require_sri_meta.js?" + Math.random() + "\"";
const WRONG_INTEGRITY =
" integrity=\"sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC\"";
const CORRECT_INEGRITY =
" integrity=\"sha384-PkcuZQHmjBQKRyv1v3x0X8qFmXiSyFyYIP+f9SU86XWvRneifdNCPg2cYFWBuKsF\"";
const POST_INTEGRITY =
" onload=\"window.parent.postMessage({result: 'script-loaded'}, '*');\"" +
" onerror=\"window.parent.postMessage({result: 'script-blocked'}, '*');\"" +
"></script>" +
"</body>" +
"</html>";
function handleRequest(request, response)
{
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/html", false);
var queryString = request.queryString;
if (queryString === "no-sri") {
response.write(PRE_INTEGRITY + POST_INTEGRITY);
return;
}
if (queryString === "wrong-sri") {
response.write(PRE_INTEGRITY + WRONG_INTEGRITY + POST_INTEGRITY);
return;
}
if (queryString === "correct-sri") {
response.write(PRE_INTEGRITY + CORRECT_INEGRITY + POST_INTEGRITY);
return;
}
// we should never get here, but just in case
// return something unexpected
response.write("do'h");
}

Просмотреть файл

@ -182,6 +182,8 @@ support-files =
file_sandbox_10.html
file_sandbox_11.html
file_sandbox_12.html
file_require_sri_meta.sjs
file_require_sri_meta.js
[test_base-uri.html]
[test_blob_data_schemes.html]
@ -272,3 +274,4 @@ tags = mcb
[test_iframe_sandbox_top_1.html]
[test_sandbox.html]
[test_ping.html]
[test_require_sri_meta.html]

Просмотреть файл

@ -0,0 +1,77 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Bug 1277557 - CSP require-sri-for does not block when CSP is in meta tag</title>
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<iframe style="width:100%;" id="testframe"></iframe>
<script class="testbody" type="text/javascript">
/* Description of the test:
* We load scripts within an iframe and make sure that meta-csp of
* require-sri-for applies correctly to preloaded scripts.
* Please note that we have to use <script src=""> to kick
* off the html preloader.
*/
SimpleTest.waitForExplicitFinish();
SpecialPowers.setBoolPref("security.csp.experimentalEnabled", true);
var curTest;
var counter = -1;
const tests = [
{ // test 1
description: "script with *no* SRI should be blocked",
query: "no-sri",
expected: "script-blocked"
},
{ // test 2
description: "script-with *incorrect* SRI should be blocked",
query: "wrong-sri",
expected: "script-blocked"
},
{ // test 3
description: "script-with *correct* SRI should be loaded",
query: "correct-sri",
expected: "script-loaded"
},
];
function finishTest() {
window.removeEventListener("message", receiveMessage, false);
SimpleTest.finish();
}
function checkResults(result) {
is(result, curTest.expected, curTest.description);
loadNextTest();
}
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
checkResults(event.data.result);
}
function loadNextTest() {
counter++;
if (counter == tests.length) {
finishTest();
return;
}
curTest = tests[counter];
var testframe = document.getElementById("testframe");
testframe.src = "file_require_sri_meta.sjs?" + curTest.query;
}
loadNextTest();
</script>
</body>
</html>

Просмотреть файл

@ -621,12 +621,19 @@ BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
clearRect = mInvalidRect;
}
// Prevent CreateRenderTargetForWindow from clearing unwanted area.
gfxUtils::ClipToRegion(mDrawTarget,
mInvalidRegion.ToUnknownRegion());
// Setup an intermediate render target to buffer all compositing. We will
// copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()
RefPtr<CompositingRenderTarget> target =
CreateRenderTargetForWindow(mInvalidRect,
clearRect,
bufferMode);
mDrawTarget->PopClip();
if (!target) {
if (!mTarget) {
mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);

Просмотреть файл

@ -2710,13 +2710,12 @@ class BaseCompiler
#if defined(JS_CODEGEN_X64)
// Copied from CodeGenerator-x64.cpp
// TODO / CLEANUP - share with the code generator.
wasm::MemoryAccess
AsmJSMemoryAccess(uint32_t before, wasm::MemoryAccess::OutOfBoundsBehavior throwBehavior,
uint32_t offsetWithinWholeSimdVector = 0)
MemoryAccess
WasmMemoryAccess(uint32_t before)
{
return wasm::MemoryAccess(before, throwBehavior, wasm::MemoryAccess::WrapOffset,
offsetWithinWholeSimdVector);
if (isCompilingAsmJS())
return MemoryAccess(before, MemoryAccess::CarryOn, MemoryAccess::WrapOffset);
return MemoryAccess(before, MemoryAccess::Throw, MemoryAccess::DontWrapOffset);
}
#endif
@ -2760,6 +2759,11 @@ class BaseCompiler
#endif
void loadHeap(const MWasmMemoryAccess& access, RegI32 ptr, AnyReg dest) {
if (access.offset() > INT32_MAX) {
masm.jump(wasm::JumpTarget::OutOfBounds);
return;
}
#if defined(JS_CODEGEN_X64)
// CodeGeneratorX64::visitAsmJSLoadHeap()
@ -2783,7 +2787,7 @@ class BaseCompiler
}
uint32_t after = masm.size();
masm.append(AsmJSMemoryAccess(before, wasm::MemoryAccess::CarryOn));
masm.append(WasmMemoryAccess(before));
verifyHeapAccessDisassembly(before, after, IsLoad(true), access.accessType(), 0, srcAddr, dest);
#else
MOZ_CRASH("BaseCompiler platform hook: loadHeap");
@ -2814,7 +2818,7 @@ class BaseCompiler
}
uint32_t after = masm.size();
masm.append(AsmJSMemoryAccess(before, wasm::MemoryAccess::CarryOn));
masm.append(WasmMemoryAccess(before));
verifyHeapAccessDisassembly(before, after, IsLoad(false), access.accessType(), 0, dstAddr, src);
#else
MOZ_CRASH("BaseCompiler platform hook: storeHeap");
@ -5210,8 +5214,7 @@ BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
// TODO / OPTIMIZE: Disable bounds checking on constant accesses
// below the minimum heap length.
MWasmMemoryAccess access(viewType, addr.align);
access.setOffset(addr.offset);
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
switch (type) {
case ValType::I32: {
@ -5260,8 +5263,7 @@ BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
// TODO / OPTIMIZE: Disable bounds checking on constant accesses
// below the minimum heap length.
MWasmMemoryAccess access(viewType, addr.align);
access.setOffset(addr.offset);
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
switch (resultType) {
case ValType::I32: {
@ -5540,8 +5542,7 @@ BaseCompiler::emitStoreWithCoercion(ValType resultType, Scalar::Type viewType)
// TODO / OPTIMIZE: Disable bounds checking on constant accesses
// below the minimum heap length.
MWasmMemoryAccess access(viewType, addr.align);
access.setOffset(addr.offset);
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
if (resultType == ValType::F32 && viewType == Scalar::Float64) {
RegF32 rv = popF32();

Просмотреть файл

@ -594,7 +594,16 @@ class FunctionCompiler
{
if (inDeadCode())
return nullptr;
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
MInstruction* load = nullptr;
if (mg().kind == ModuleKind::Wasm) {
if (!mg().usesSignal.forOOB)
curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
load = MWasmLoad::New(alloc(), base, access);
} else {
load = MAsmJSLoadHeap::New(alloc(), base, access);
}
curBlock_->add(load);
return load;
}
@ -603,7 +612,16 @@ class FunctionCompiler
{
if (inDeadCode())
return;
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
MInstruction* store = nullptr;
if (mg().kind == ModuleKind::Wasm) {
if (!mg().usesSignal.forOOB)
curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
store = MWasmStore::New(alloc(), base, access, v);
} else {
store = MAsmJSStoreHeap::New(alloc(), base, access, v);
}
curBlock_->add(store);
}
@ -2055,32 +2073,6 @@ EmitSelect(FunctionCompiler& f)
return true;
}
enum class IsAtomic {
No = false,
Yes = true
};
static bool
SetHeapAccessOffset(FunctionCompiler& f, uint32_t offset, MWasmMemoryAccess* access,
MDefinition** base, IsAtomic atomic = IsAtomic::No)
{
// TODO Remove this after implementing non-wraparound offset semantics.
uint32_t endOffset = offset + access->byteSize();
if (endOffset < offset)
return false;
// Assume worst case.
if (endOffset > f.mirGen().foldableOffsetRange(/* bounds check */ true, bool(atomic))) {
MDefinition* rhs = f.constant(Int32Value(offset), MIRType::Int32);
*base = f.binary<MAdd>(*base, rhs, MIRType::Int32);
access->setOffset(0);
} else {
access->setOffset(offset);
}
return true;
}
static bool
EmitLoad(FunctionCompiler& f, ValType type, Scalar::Type viewType)
{
@ -2088,13 +2080,8 @@ EmitLoad(FunctionCompiler& f, ValType type, Scalar::Type viewType)
if (!f.iter().readLoad(type, Scalar::byteSize(viewType), &addr))
return false;
MWasmMemoryAccess access(viewType, addr.align);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
return false;
f.iter().setResult(f.loadHeap(base, access));
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
f.iter().setResult(f.loadHeap(addr.base, access));
return true;
}
@ -2106,13 +2093,8 @@ EmitStore(FunctionCompiler& f, ValType resultType, Scalar::Type viewType)
if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr, &value))
return false;
MWasmMemoryAccess access(viewType, addr.align);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
return false;
f.storeHeap(base, access, value);
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
f.storeHeap(addr.base, access, value);
return true;
}
@ -2131,13 +2113,8 @@ EmitStoreWithCoercion(FunctionCompiler& f, ValType resultType, Scalar::Type view
else
MOZ_CRASH("unexpected coerced store");
MWasmMemoryAccess access(viewType, addr.align);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
return false;
f.storeHeap(base, access, value);
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
f.storeHeap(addr.base, access, value);
return true;
}
@ -2207,13 +2184,9 @@ EmitAtomicsLoad(FunctionCompiler& f)
if (!f.iter().readAtomicLoad(&addr, &viewType))
return false;
MWasmMemoryAccess access(viewType, addr.align, 0, MembarBeforeLoad, MembarAfterLoad);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
return false;
f.iter().setResult(f.loadAtomicHeap(base, access));
MWasmMemoryAccess access(viewType, addr.align, addr.offset, 0,
MembarBeforeLoad, MembarAfterLoad);
f.iter().setResult(f.loadAtomicHeap(addr.base, access));
return true;
}
@ -2226,13 +2199,9 @@ EmitAtomicsStore(FunctionCompiler& f)
if (!f.iter().readAtomicStore(&addr, &viewType, &value))
return false;
MWasmMemoryAccess access(viewType, addr.align, 0, MembarBeforeStore, MembarAfterStore);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
return false;
f.storeAtomicHeap(base, access, value);
MWasmMemoryAccess access(viewType, addr.align, addr.offset, 0,
MembarBeforeStore, MembarAfterStore);
f.storeAtomicHeap(addr.base, access, value);
f.iter().setResult(value);
return true;
}
@ -2247,13 +2216,8 @@ EmitAtomicsBinOp(FunctionCompiler& f)
if (!f.iter().readAtomicBinOp(&addr, &viewType, &op, &value))
return false;
MWasmMemoryAccess access(viewType, addr.align);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
return false;
f.iter().setResult(f.atomicBinopHeap(op, base, access, value));
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
f.iter().setResult(f.atomicBinopHeap(op, addr.base, access, value));
return true;
}
@ -2267,13 +2231,8 @@ EmitAtomicsCompareExchange(FunctionCompiler& f)
if (!f.iter().readAtomicCompareExchange(&addr, &viewType, &oldValue, &newValue))
return false;
MWasmMemoryAccess access(viewType, addr.align);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
return false;
f.iter().setResult(f.atomicCompareExchangeHeap(base, access, oldValue, newValue));
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
f.iter().setResult(f.atomicCompareExchangeHeap(addr.base, access, oldValue, newValue));
return true;
}
@ -2286,13 +2245,8 @@ EmitAtomicsExchange(FunctionCompiler& f)
if (!f.iter().readAtomicExchange(&addr, &viewType, &value))
return false;
MWasmMemoryAccess access(viewType, addr.align);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
return false;
f.iter().setResult(f.atomicExchangeHeap(base, access, value));
MWasmMemoryAccess access(viewType, addr.align, addr.offset);
f.iter().setResult(f.atomicExchangeHeap(addr.base, access, value));
return true;
}
@ -2513,13 +2467,8 @@ EmitSimdLoad(FunctionCompiler& f, ValType resultType, unsigned numElems)
if (!f.iter().readLoad(resultType, Scalar::byteSize(viewType), &addr))
return false;
MWasmMemoryAccess access(viewType, addr.align, numElems);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
return false;
f.iter().setResult(f.loadSimdHeap(base, access));
MWasmMemoryAccess access(viewType, addr.align, addr.offset, numElems);
f.iter().setResult(f.loadSimdHeap(addr.base, access));
return true;
}
@ -2537,13 +2486,8 @@ EmitSimdStore(FunctionCompiler& f, ValType resultType, unsigned numElems)
if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr, &value))
return false;
MWasmMemoryAccess access(viewType, addr.align, numElems);
MDefinition* base = addr.base;
if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
return false;
f.storeSimdHeap(base, access, value);
MWasmMemoryAccess access(viewType, addr.align, addr.offset, numElems);
f.storeSimdHeap(addr.base, access, value);
return true;
}

Просмотреть файл

@ -56,7 +56,7 @@ struct LinkData : LinkDataCacheablePod
RawPointer,
CodeLabel,
InstructionImmediate
};
};
MOZ_INIT_OUTSIDE_CTOR uint32_t patchAtOffset;
MOZ_INIT_OUTSIDE_CTOR uint32_t targetOffset;

Просмотреть файл

@ -650,6 +650,13 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
MOZ_RELEASE_ASSERT(accessAddress + access.size() > instance.memoryBase() + instance.memoryLength(),
"Computed access address is not actually out of bounds");
// Wasm loads/stores don't wrap offsets at all, so hitting the guard page
// means we are out of bounds in any cases.
if (!memoryAccess->wrapOffset()) {
MOZ_ASSERT(memoryAccess->throwOnOOB());
return instance.codeSegment().outOfBoundsCode();
}
// The basic sandbox model is that all heap accesses are a heap base
// register plus an index, and the index is always computed with 32-bit
// operations, so we know it can only be 4 GiB off of the heap base.
@ -733,8 +740,11 @@ MOZ_COLD static uint8_t*
EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
const MemoryAccess* memoryAccess, const Instance& instance)
{
// TODO: Implement unaligned accesses.
return instance.codeSegment().outOfBoundsCode();
// We forbid ARM instruction sets below ARMv7, so that solves unaligned
// integer memory accesses. So the only way to land here is because of a
// non-default configured kernel or an unaligned floating-point access.
// TODO Handle FPU unaligned accesses on ARM (bug 1283121).
return instance.codeSegment().unalignedAccessCode();
}
#endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_UNALIGNED)
@ -1097,16 +1107,28 @@ MachExceptionHandler::install(JSRuntime* rt)
#else // If not Windows or Mac, assume Unix
enum class Signal {
SegFault,
BusError
};
// Be very cautious and default to not handling; we don't want to accidentally
// silence real crashes from real bugs.
template<Signal signal>
static bool
HandleFault(int signum, siginfo_t* info, void* ctx)
{
// The signals we're expecting come from access violations, accessing
// mprotected memory. If the signal originates anywhere else, don't try
// to handle it.
MOZ_RELEASE_ASSERT(signum == SIGSEGV);
if (info->si_code != SEGV_ACCERR)
if (signal == Signal::SegFault)
MOZ_RELEASE_ASSERT(signum == SIGSEGV);
else
MOZ_RELEASE_ASSERT(signum == SIGBUS);
if (signal == Signal::SegFault && info->si_code != SEGV_ACCERR)
return false;
if (signal == Signal::BusError && info->si_code != BUS_ADRALN)
return false;
CONTEXT* context = (CONTEXT*)ctx;
@ -1135,7 +1157,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
return false;
const MemoryAccess* memoryAccess = instance.lookupMemoryAccess(pc);
if (!memoryAccess)
if (signal == Signal::SegFault && !memoryAccess)
return false;
*ppc = EmulateHeapAccess(context, pc, faultingAddress, memoryAccess, instance);
@ -1144,13 +1166,19 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
}
static struct sigaction sPrevSEGVHandler;
static struct sigaction sPrevSIGBUSHandler;
template<Signal signal>
static void
AsmJSFaultHandler(int signum, siginfo_t* info, void* context)
{
if (HandleFault(signum, info, context))
if (HandleFault<signal>(signum, info, context))
return;
struct sigaction* previousSignal = signal == Signal::SegFault
? &sPrevSEGVHandler
: &sPrevSIGBUSHandler;
// This signal is not for any asm.js code we expect, so we need to forward
// the signal to the next handler. If there is no next handler (SIG_IGN or
// SIG_DFL), then it's time to crash. To do this, we set the signal back to
@ -1163,15 +1191,14 @@ AsmJSFaultHandler(int signum, siginfo_t* info, void* context)
// signal to it's original disposition and returning.
//
// Note: the order of these tests matter.
if (sPrevSEGVHandler.sa_flags & SA_SIGINFO)
sPrevSEGVHandler.sa_sigaction(signum, info, context);
else if (sPrevSEGVHandler.sa_handler == SIG_DFL || sPrevSEGVHandler.sa_handler == SIG_IGN)
sigaction(signum, &sPrevSEGVHandler, nullptr);
if (previousSignal->sa_flags & SA_SIGINFO)
previousSignal->sa_sigaction(signum, info, context);
else if (previousSignal->sa_handler == SIG_DFL || previousSignal->sa_handler == SIG_IGN)
sigaction(signum, previousSignal, nullptr);
else
sPrevSEGVHandler.sa_handler(signum);
previousSignal->sa_handler(signum);
}
#endif
# endif // XP_WIN || XP_DARWIN || assume unix
#endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS)
static void
@ -1303,12 +1330,22 @@ wasm::EnsureSignalHandlersInstalled(JSRuntime* rt)
// SA_NODEFER allows us to reenter the signal handler if we crash while
// handling the signal, and fall through to the Breakpad handler by testing
// handlingSegFault.
# if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
struct sigaction faultHandler;
faultHandler.sa_flags = SA_SIGINFO | SA_NODEFER;
faultHandler.sa_sigaction = &AsmJSFaultHandler;
faultHandler.sa_sigaction = &AsmJSFaultHandler<Signal::SegFault>;
sigemptyset(&faultHandler.sa_mask);
if (sigaction(SIGSEGV, &faultHandler, &sPrevSEGVHandler))
MOZ_CRASH("unable to install segv handler");
# elif defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_UNALIGNED)
struct sigaction busHandler;
busHandler.sa_flags = SA_SIGINFO | SA_NODEFER;
busHandler.sa_sigaction = &AsmJSFaultHandler<Signal::BusError>;
sigemptyset(&busHandler.sa_mask);
if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler))
MOZ_CRASH("unable to install sigbus handler");
# endif
# endif
#endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS)

Просмотреть файл

@ -1042,8 +1042,10 @@ js::GCMarker::eagerlyMarkChildren(Shape* shape)
// be traced by this loop they do not need to be traced here as well.
BaseShape* base = shape->base();
CheckTraversedEdge(shape, base);
if (mark(base))
if (mark(base)) {
MOZ_ASSERT(base->canSkipMarkingShapeTable(shape));
base->traceChildrenSkipShapeTable(this);
}
traverseEdge(shape, shape->propidRef().get());

Просмотреть файл

@ -1,74 +1,65 @@
// |jit-test| test-also-wasm-baseline
load(libdir + "wasm.js");
function loadModule(type, ext, offset, align) {
return wasmEvalText(
`(module
(memory 1
(segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")
(segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")
)
(func (param i32) (result ${type})
(${type}.load${ext}
offset=${offset}
${align != 0 ? 'align=' + align : ''}
(get_local 0)
)
) (export "" 0))`
);
}
function storeModule(type, ext, offset, align) {
var load_ext = ext === '' ? '' : ext + '_s';
return wasmEvalText(
`(module
(memory 1
(segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")
(segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")
)
(func (param i32) (param ${type}) (result ${type})
(${type}.store${ext}
offset=${offset}
${align != 0 ? 'align=' + align : ''}
(get_local 0)
(get_local 1)
)
) (export "store" 0)
(func (param i32) (result ${type})
(${type}.load${load_ext}
offset=${offset}
${align != 0 ? 'align=' + align : ''}
(get_local 0)
)
) (export "load" 1))`
);
}
function testLoad(type, ext, base, offset, align, expect) {
assertEq(wasmEvalText(
'(module' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
' (func (param i32) (result ' + type + ')' +
' (' + type + '.load' + ext +
' offset=' + offset +
' ' + (align != 0 ? 'align=' + align : '') +
' (get_local 0)' +
' )' +
' ) (export "" 0))'
)(base), expect);
assertEq(loadModule(type, ext, offset, align)(base), expect);
}
function testLoadOOB(type, ext, base, offset, align) {
assertErrorMessage(() => loadModule(type, ext, offset, align)(base), Error, /invalid or out-of-range index/);
}
function testStore(type, ext, base, offset, align, value) {
assertEq(wasmEvalText(
'(module' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
' (func (param i32) (param ' + type + ') (result ' + type + ')' +
' (' + type + '.store' + ext +
' offset=' + offset +
' ' + (align != 0 ? 'align=' + align : '') +
' (get_local 0)' +
' (get_local 1)' +
' )' +
' ) (export "" 0))'
)(base, value), value);
let module = storeModule(type, ext, offset, align);
assertEq(module.store(base, value), value);
assertEq(module.load(base), value);
}
function testLoadError(type, ext, base, offset, align, errorMsg) {
assertErrorMessage(() => wasmEvalText(
'(module' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
' (func (param i32) (result ' + type + ')' +
' (' + type + '.load' + ext +
' offset=' + offset +
' ' + (align != 0 ? 'align=' + align : '') +
' (get_local 0)' +
' )' +
' ) (export "" 0))'
), Error, errorMsg);
}
function testStoreError(type, ext, base, offset, align, errorMsg) {
assertErrorMessage(() => wasmEvalText(
'(module' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
' (func (param i32) (param ' + type + ') (result ' + type + ')' +
' (' + type + '.store' + ext +
' offset=' + offset +
' ' + (align != 0 ? 'align=' + align : '') +
' (get_local 0)' +
' (get_local 1)' +
' )' +
' ) (export "" 0))'
), Error, errorMsg);
function testStoreOOB(type, ext, base, offset, align, value) {
assertErrorMessage(() => storeModule(type, ext, offset, align).store(base, value), Error, /invalid or out-of-range index/);
}
testLoad('i32', '', 0, 0, 0, 0x03020100);
@ -143,21 +134,70 @@ testStore('f64', '', 1, 7, 4, 0.89012345);
testStore('i32', '8', 0, 0, 0, 0x23);
testStore('i32', '16', 0, 0, 0, 0x2345);
testLoadError('i32', '8_s', 0, 0, 2, /greater than natural alignment/);
testLoadError('i32', '8_u', 0, 0, 2, /greater than natural alignment/);
testLoadError('i32', '16_s', 0, 0, 4, /greater than natural alignment/);
testLoadError('i32', '16_u', 0, 0, 4, /greater than natural alignment/);
testLoadError('i32', '', 0, 0, 8, /greater than natural alignment/);
testLoadError('f32', '', 0, 0, 8, /greater than natural alignment/);
testLoadError('f64', '', 0, 0, 16, /greater than natural alignment/);
testStoreError('i32', '8', 0, 0, 2, /greater than natural alignment/);
testStoreError('i32', '16', 0, 0, 4, /greater than natural alignment/);
testStoreError('i32', '', 0, 0, 8, /greater than natural alignment/);
testStoreError('f32', '', 0, 0, 8, /greater than natural alignment/);
testStoreError('f64', '', 0, 0, 16, /greater than natural alignment/);
assertErrorMessage(() => wasmEvalText('(module (memory 2 1))'), TypeError, /maximum memory size less than initial memory size/);
// Test bounds checks and edge cases.
const align = 0;
for (let offset of [0, 1, 2, 3, 4, 8, 16, 41, 0xfff8]) {
// Accesses of 1 byte.
let lastValidIndex = 0x10000 - 1 - offset;
testLoad('i32', '8_s', lastValidIndex, offset, align, 0);
testLoadOOB('i32', '8_s', lastValidIndex + 1, offset, align);
testLoad('i32', '8_u', lastValidIndex, offset, align, 0);
testLoadOOB('i32', '8_u', lastValidIndex + 1, offset, align);
testStore('i32', '8', lastValidIndex, offset, align, -42);
testStoreOOB('i32', '8', lastValidIndex + 1, offset, align, -42);
// Accesses of 2 bytes.
lastValidIndex = 0x10000 - 2 - offset;
testLoad('i32', '16_s', lastValidIndex, offset, align, 0);
testLoadOOB('i32', '16_s', lastValidIndex + 1, offset, align);
testLoad('i32', '16_u', lastValidIndex, offset, align, 0);
testLoadOOB('i32', '16_u', lastValidIndex + 1, offset, align);
testStore('i32', '16', lastValidIndex, offset, align, -32768);
testStoreOOB('i32', '16', lastValidIndex + 1, offset, align, -32768);
// Accesses of 4 bytes.
lastValidIndex = 0x10000 - 4 - offset;
testLoad('i32', '', lastValidIndex, offset, align, 0);
testLoadOOB('i32', '', lastValidIndex + 1, offset, align);
testLoad('f32', '', lastValidIndex, offset, align, 0);
testLoadOOB('f32', '', lastValidIndex + 1, offset, align);
testStore('i32', '', lastValidIndex, offset, align, 1337);
testStoreOOB('i32', '', lastValidIndex + 1, offset, align, 1337);
testStore('f32', '', lastValidIndex, offset, align, Math.fround(13.37));
testStoreOOB('f32', '', lastValidIndex + 1, offset, align, Math.fround(13.37));
// Accesses of 8 bytes.
lastValidIndex = 0x10000 - 8 - offset;
testLoad('f64', '', lastValidIndex, offset, align, 0);
testLoadOOB('f64', '', lastValidIndex + 1, offset, align);
testStore('f64', '', lastValidIndex, offset, align, 1.23456789);
testStoreOOB('f64', '', lastValidIndex + 1, offset, align, 1.23456789);
}
// Ensure wrapping doesn't apply.
offset = 0x7fffffff; // maximum allowed offset that doesn't always throw.
for (let index of [0, 1, 2, 3, 0x7fffffff, 0x80000000, 0x80000001]) {
testLoadOOB('i32', '8_s', index, offset, align);
testLoadOOB('i32', '16_s', index, offset, align);
testLoadOOB('i32', '', index, offset, align);
testLoadOOB('f32', '', index, offset, align);
testLoadOOB('f64', '', index, offset, align);
}
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "f64"));
@ -170,3 +210,44 @@ assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (i32.store offse
wasmEvalText('(module (memory 0 65535))')
assertErrorMessage(() => wasmEvalText('(module (memory 0 65536))'), TypeError, /maximum memory size too big/);
// Test high charge of registers
function testRegisters() {
assertEq(wasmEvalText(
`(module
(memory 1
(segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")
(segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")
)
(func (param i32) (local i32 i32 i32 i32 f32 f64) (result i32)
(set_local 1 (i32.load8_s offset=4 (get_local 0)))
(set_local 2 (i32.load16_s (get_local 1)))
(i32.store8 offset=4 (get_local 0) (get_local 1))
(set_local 3 (i32.load16_u (get_local 2)))
(i32.store16 (get_local 1) (get_local 2))
(set_local 4 (i32.load (get_local 2)))
(i32.store (get_local 1) (get_local 2))
(set_local 5 (f32.load (get_local 4)))
(f32.store (get_local 4) (get_local 5))
(set_local 6 (f64.load (get_local 4)))
(f64.store (get_local 4) (get_local 6))
(i32.add
(i32.add
(get_local 0)
(get_local 1)
)
(i32.add
(i32.add
(get_local 2)
(get_local 3)
)
(i32.add
(get_local 4)
(i32.reinterpret/f32 (get_local 5))
)
)
)
) (export "" 0))`
)(1), 50464523);
}
testRegisters();

Просмотреть файл

@ -1,4 +1,2 @@
// |jit-test| test-also-wasm-baseline
// TODO: wrapping offsets
quit();
var importedArgs = ['address.wast']; load(scriptdir + '../spec.js');

Просмотреть файл

@ -1,4 +1,4 @@
// |jit-test| test-also-wasm-baseline
// TODO unaligned memory accesses
// TODO i64 loads
quit();
var importedArgs = ['memory.wast']; load(scriptdir + '../spec.js');

Просмотреть файл

@ -1,4 +1,4 @@
// |jit-test| test-also-wasm-baseline
// TODO trap on OOB
// TODO dce'd effectful instructions
quit();
var importedArgs = ['traps.wast']; load(scriptdir + '../spec.js');

Просмотреть файл

@ -142,6 +142,8 @@ GetObject(const MDefinition* ins)
case MDefinition::Op_AtomicTypedArrayElementBinop:
case MDefinition::Op_AsmJSLoadHeap:
case MDefinition::Op_AsmJSStoreHeap:
case MDefinition::Op_WasmLoad:
case MDefinition::Op_WasmStore:
case MDefinition::Op_AsmJSCompareExchangeHeap:
case MDefinition::Op_AsmJSAtomicBinopHeap:
case MDefinition::Op_AsmJSLoadGlobalVar:

Просмотреть файл

@ -12959,10 +12959,11 @@ class MWasmMemoryAccess
MemoryBarrierBits barrierAfter_;
public:
explicit MWasmMemoryAccess(Scalar::Type accessType, uint32_t align, unsigned numSimdElems = 0,
explicit MWasmMemoryAccess(Scalar::Type accessType, uint32_t align, uint32_t offset,
unsigned numSimdElems = 0,
MemoryBarrierBits barrierBefore = MembarNobits,
MemoryBarrierBits barrierAfter = MembarNobits)
: offset_(0),
: offset_(offset),
align_(align),
accessType_(accessType),
needsBoundsCheck_(true),
@ -12984,13 +12985,94 @@ class MWasmMemoryAccess
: TypedArrayElemSize(accessType());
}
bool needsBoundsCheck() const { return needsBoundsCheck_; }
void removeBoundsCheck() { needsBoundsCheck_ = false; }
unsigned numSimdElems() const { MOZ_ASSERT(Scalar::isSimdType(accessType_)); return numSimdElems_; }
void setOffset(uint32_t o) { offset_ = o; }
void setAlign(uint32_t a) { MOZ_ASSERT(mozilla::IsPowerOfTwo(a)); align_ = a; }
MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
bool isAtomicAccess() const { return (barrierBefore_|barrierAfter_) != MembarNobits; }
void removeBoundsCheck() { needsBoundsCheck_ = false; }
void setOffset(uint32_t o) { offset_ = o; }
};
class MWasmBoundsCheck
: public MUnaryInstruction,
public MWasmMemoryAccess,
public NoTypePolicy::Data
{
explicit MWasmBoundsCheck(MDefinition* index, const MWasmMemoryAccess& access)
: MUnaryInstruction(index),
MWasmMemoryAccess(access)
{
setMovable();
setGuard(); // Effectful: throws for OOB.
}
public:
INSTRUCTION_HEADER(WasmBoundsCheck)
TRIVIAL_NEW_WRAPPERS
bool congruentTo(const MDefinition* ins) const override {
if (!congruentIfOperandsEqual(ins))
return false;
const MWasmBoundsCheck* other = ins->toWasmBoundsCheck();
return accessType() == other->accessType() &&
offset() == other->offset() &&
align() == other->align();
}
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
class MWasmLoad
: public MUnaryInstruction,
public MWasmMemoryAccess,
public NoTypePolicy::Data
{
MWasmLoad(MDefinition* base, const MWasmMemoryAccess& access)
: MUnaryInstruction(base),
MWasmMemoryAccess(access)
{
setGuard();
MOZ_ASSERT(access.accessType() != Scalar::Uint8Clamped, "unexpected load heap in wasm");
setResultType(ScalarTypeToMIRType(access.accessType()));
}
public:
INSTRUCTION_HEADER(WasmLoad)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, base))
AliasSet getAliasSet() const override {
// When a barrier is needed, make the instruction effectful by giving
// it a "store" effect.
if (isAtomicAccess())
return AliasSet::Store(AliasSet::AsmJSHeap);
return AliasSet::Load(AliasSet::AsmJSHeap);
}
};
class MWasmStore
: public MBinaryInstruction,
public MWasmMemoryAccess,
public NoTypePolicy::Data
{
MWasmStore(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* value)
: MBinaryInstruction(base, value),
MWasmMemoryAccess(access)
{
setGuard();
}
public:
INSTRUCTION_HEADER(WasmStore)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, base), (1, value))
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::AsmJSHeap);
}
};
class MAsmJSLoadHeap

Просмотреть файл

@ -224,7 +224,6 @@ class MIRGenerator
bool needsBoundsCheckBranch(const MWasmMemoryAccess* access) const;
size_t foldableOffsetRange(const MWasmMemoryAccess* access) const;
size_t foldableOffsetRange(bool accessNeedsBoundsCheck, bool atomic) const;
private:
GraphSpewer gs_;

Просмотреть файл

@ -125,12 +125,6 @@ MIRGenerator::needsBoundsCheckBranch(const MWasmMemoryAccess* access) const
size_t
MIRGenerator::foldableOffsetRange(const MWasmMemoryAccess* access) const
{
return foldableOffsetRange(access->needsBoundsCheck(), access->isAtomicAccess());
}
size_t
MIRGenerator::foldableOffsetRange(bool accessNeedsBoundsCheck, bool atomic) const
{
// This determines whether it's ok to fold up to WasmImmediateRange
// offsets, instead of just WasmCheckedImmediateRange.
@ -148,14 +142,14 @@ MIRGenerator::foldableOffsetRange(bool accessNeedsBoundsCheck, bool atomic) cons
// Signal-handling can be dynamically disabled by OS bugs or flags.
// Bug 1254935: Atomic accesses can't be handled with signal handlers yet.
if (usesSignalHandlersForAsmJSOOB_ && !atomic)
if (usesSignalHandlersForAsmJSOOB_ && !access->isAtomicAccess())
return WasmImmediateRange;
#endif
// On 32-bit platforms, if we've proven the access is in bounds after
// 32-bit wrapping, we can fold full offsets because they're added with
// 32-bit arithmetic.
if (sizeof(intptr_t) == sizeof(int32_t) && !accessNeedsBoundsCheck)
if (sizeof(intptr_t) == sizeof(int32_t) && !access->needsBoundsCheck())
return WasmImmediateRange;
// Otherwise, only allow the checked size. This is always less than the

Просмотреть файл

@ -268,6 +268,9 @@ namespace jit {
_(IsObject) \
_(HasClass) \
_(CopySign) \
_(WasmBoundsCheck) \
_(WasmLoad) \
_(WasmStore) \
_(WasmTruncateToInt32) \
_(AsmJSNeg) \
_(AsmJSUnsignedToDouble) \

Просмотреть файл

@ -1206,6 +1206,7 @@ class Assembler : public AssemblerShared
LessThan = LT,
LessThanOrEqual = LE,
Overflow = VS,
CarrySet = CS,
Signed = MI,
NotSigned = PL,
Zero = EQ,

Просмотреть файл

@ -2188,6 +2188,119 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
memoryBarrier(mir->barrierAfter());
}
void
CodeGeneratorARM::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
{
MWasmBoundsCheck* mir = ins->mir();
uint32_t offset = mir->offset();
if (offset > INT32_MAX) {
masm.as_b(wasm::JumpTarget::OutOfBounds);
return;
}
// No guarantee that heapBase + endOffset can be properly encoded in
// the cmp immediate in ma_BoundsCheck, so use an explicit add instead.
uint32_t endOffset = mir->endOffset();
Register ptr = ToRegister(ins->ptr());
ScratchRegisterScope ptrPlusOffset(masm);
masm.move32(Imm32(endOffset), ptrPlusOffset);
masm.ma_add(ptr, ptrPlusOffset, SetCC);
// Detect unsigned overflow by checking the carry bit.
masm.as_b(wasm::JumpTarget::OutOfBounds, Assembler::CarrySet);
uint32_t cmpOffset = masm.ma_BoundsCheck(ptrPlusOffset).getOffset();
masm.append(wasm::BoundsCheck(cmpOffset));
masm.as_b(wasm::JumpTarget::OutOfBounds, Assembler::Above);
}
void
CodeGeneratorARM::visitWasmLoad(LWasmLoad* lir)
{
const MWasmLoad* mir = lir->mir();
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
uint32_t offset = mir->offset();
if (offset > INT32_MAX) {
// This is unreachable because of bounds checks.
masm.breakpoint();
return;
}
Register ptr = ToRegister(lir->ptr());
AnyRegister output = ToAnyRegister(lir->output());
// Maybe add the offset.
if (offset) {
Register ptrPlusOffset = ToRegister(lir->ptrCopy());
masm.ma_add(Imm32(offset), ptrPlusOffset);
ptr = ptrPlusOffset;
} else {
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
}
Scalar::Type type = mir->accessType();
bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32;
bool isFloat = output.isFloat();
unsigned byteSize = mir->byteSize();
if (isFloat) {
MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
ScratchRegisterScope scratch(masm);
masm.ma_add(HeapReg, ptr, scratch);
masm.ma_vldr(Address(scratch, 0), output.fpu());
} else {
masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.gpr());
}
}
void
CodeGeneratorARM::visitWasmStore(LWasmStore* lir)
{
const MWasmStore* mir = lir->mir();
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
uint32_t offset = mir->offset();
if (offset > INT32_MAX) {
// This is unreachable because of bounds checks.
masm.breakpoint();
return;
}
Register ptr = ToRegister(lir->ptr());
// Maybe add the offset.
if (offset) {
Register ptrPlusOffset = ToRegister(lir->ptrCopy());
masm.ma_add(Imm32(offset), ptrPlusOffset);
ptr = ptrPlusOffset;
} else {
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
}
AnyRegister value = ToAnyRegister(lir->value());
unsigned byteSize = mir->byteSize();
Scalar::Type type = mir->accessType();
if (value.isFloat()) {
FloatRegister val = value.fpu();
MOZ_ASSERT((byteSize == 4) == val.isSingle());
ScratchRegisterScope scratch(masm);
masm.ma_add(HeapReg, ptr, scratch);
masm.ma_vstr(val, Address(scratch, 0));
} else {
bool isSigned = type == Scalar::Uint32 || type == Scalar::Int32; // see AsmJSStoreHeap;
Register val = value.gpr();
masm.ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, HeapReg, ptr, val);
}
}
void
CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
{

Просмотреть файл

@ -203,6 +203,9 @@ class CodeGeneratorARM : public CodeGeneratorShared
void visitAsmSelect(LAsmSelect* ins);
void visitAsmReinterpret(LAsmReinterpret* ins);
void visitAsmJSCall(LAsmJSCall* ins);
void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
void visitWasmLoad(LWasmLoad* ins);
void visitWasmStore(LWasmStore* ins);
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);

Просмотреть файл

@ -497,6 +497,48 @@ LIRGeneratorARM::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
define(lir, ins);
}
void
LIRGeneratorARM::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
{
MDefinition* input = ins->input();
MOZ_ASSERT(input->type() == MIRType::Int32);
LAllocation baseAlloc = useRegisterAtStart(input);
auto* lir = new(alloc()) LWasmBoundsCheck(baseAlloc);
add(lir, ins);
}
void
LIRGeneratorARM::visitWasmLoad(MWasmLoad* ins)
{
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
LAllocation baseAlloc = useRegisterAtStart(base);
auto* lir = new(alloc()) LWasmLoad(baseAlloc);
if (ins->offset())
lir->setTemp(0, tempCopy(base, 0));
define(lir, ins);
}
void
LIRGeneratorARM::visitWasmStore(MWasmStore* ins)
{
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
LAllocation baseAlloc = useRegisterAtStart(base);
LAllocation valueAlloc = useRegisterAtStart(ins->value());
auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc);
if (ins->offset())
lir->setTemp(0, tempCopy(base, 0));
add(lir, ins);
}
void
LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{

Просмотреть файл

@ -97,6 +97,9 @@ class LIRGeneratorARM : public LIRGeneratorShared
void visitAsmSelect(MAsmSelect* ins);
void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);
void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
void visitWasmLoad(MWasmLoad* ins);
void visitWasmStore(MWasmStore* ins);
void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);

Просмотреть файл

@ -324,6 +324,24 @@ LIRGeneratorARM64::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
MOZ_CRASH("NY");
}
void
LIRGeneratorARM64::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
{
MOZ_CRASH("NY");
}
void
LIRGeneratorARM64::visitWasmLoad(MWasmLoad* ins)
{
MOZ_CRASH("NY");
}
void
LIRGeneratorARM64::visitWasmStore(MWasmStore* ins)
{
MOZ_CRASH("NY");
}
void
LIRGeneratorARM64::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
{

Просмотреть файл

@ -113,6 +113,9 @@ class LIRGeneratorARM64 : public LIRGeneratorShared
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
void visitWasmLoad(MWasmLoad* ins);
void visitWasmStore(MWasmStore* ins);
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
void visitCopySign(MCopySign* ins);
};

Просмотреть файл

@ -61,7 +61,7 @@ class Token {
virtual bool IsUnknown() const { return false; }
// Token properties.
virtual bool CanAddressMemory() const { return false; }
virtual uint8_t* ToAddress(Debugger* debugger) const;
virtual uint8_t* ToAddress(Debugger* debugger) const = 0;
virtual void Print(FILE* out = stdout) const = 0;
static Token* Tokenize(const char* arg);
@ -77,6 +77,11 @@ template<typename T> class ValueToken : public Token {
T value() const { return value_; }
virtual uint8_t* ToAddress(Debugger* debugger) const {
USE(debugger);
VIXL_ABORT();
}
protected:
T value_;
};
@ -202,6 +207,11 @@ class FormatToken : public Token {
virtual void PrintData(void* data, FILE* out = stdout) const = 0;
virtual void Print(FILE* out = stdout) const = 0;
virtual uint8_t* ToAddress(Debugger* debugger) const {
USE(debugger);
VIXL_ABORT();
}
static Token* Tokenize(const char* arg);
static FormatToken* Cast(Token* tok) {
VIXL_ASSERT(tok->IsFormat());
@ -237,6 +247,10 @@ class UnknownToken : public Token {
strncpy(unknown_, arg, size);
}
virtual ~UnknownToken() { js_free(unknown_); }
virtual uint8_t* ToAddress(Debugger* debugger) const {
USE(debugger);
VIXL_ABORT();
}
virtual bool IsUnknown() const { return true; }
virtual void Print(FILE* out = stdout) const;
@ -797,13 +811,6 @@ static bool StringToInt64(int64_t* value, const char* line, int base = 10) {
}
uint8_t* Token::ToAddress(Debugger* debugger) const {
USE(debugger);
VIXL_UNREACHABLE();
return NULL;
}
Token* Token::Tokenize(const char* arg) {
if ((arg == NULL) || (*arg == '\0')) {
return NULL;

Просмотреть файл

@ -24,6 +24,8 @@
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef JS_SIMULATOR_ARM64
#ifndef VIXL_A64_DEBUGGER_A64_H_
#define VIXL_A64_DEBUGGER_A64_H_
@ -111,3 +113,5 @@ class Debugger : public Simulator {
} // namespace vixl
#endif // VIXL_A64_DEBUGGER_A64_H_
#endif // JS_SIMULATOR_ARM64

Просмотреть файл

@ -37,7 +37,7 @@
// List macro containing all visitors needed by the decoder class.
#define VISITOR_LIST(V) \
#define VISITOR_LIST_THAT_RETURN(V) \
V(PCRelAddressing) \
V(AddSubImmediate) \
V(LogicalImmediate) \
@ -106,8 +106,14 @@
V(NEONShiftImmediate) \
V(NEONTable) \
V(NEONPerm) \
V(Unallocated) \
V(Unimplemented)
#define VISITOR_LIST_THAT_DONT_RETURN(V) \
V(Unallocated) \
V(Unimplemented) \
#define VISITOR_LIST(V) \
VISITOR_LIST_THAT_RETURN(V) \
VISITOR_LIST_THAT_DONT_RETURN(V) \
namespace vixl {

Просмотреть файл

@ -24,6 +24,8 @@
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef JS_SIMULATOR_ARM64
#include <cmath>
#include "jit/arm64/vixl/Simulator-vixl.h"
@ -4872,3 +4874,5 @@ LogicVRegister Simulator::ucvtf(VectorFormat vform,
} // namespace vixl
#endif // JS_SIMULATOR_ARM64

Просмотреть файл

@ -26,6 +26,8 @@
#include "jit/arm64/vixl/MacroAssembler-vixl.h"
#include <ctype.h>
namespace vixl {
MacroAssembler::MacroAssembler()

Просмотреть файл

@ -33,6 +33,8 @@
#include "jit/arm64/Assembler-arm64.h"
#include "jit/arm64/vixl/Debugger-vixl.h"
#include "jit/arm64/vixl/Globals-vixl.h"
#include "jit/arm64/vixl/Instrument-vixl.h"
#include "jit/arm64/vixl/Simulator-Constants-vixl.h"
#define LS_MACRO_LIST(V) \
V(Ldrb, Register&, rt, LDRB_w) \

Просмотреть файл

@ -0,0 +1,141 @@
// Copyright 2015, ARM Limited
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of ARM Limited nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef VIXL_A64_SIMULATOR_CONSTANTS_A64_H_
#define VIXL_A64_SIMULATOR_CONSTANTS_A64_H_
namespace vixl {
// Debug instructions.
//
// VIXL's macro-assembler and simulator support a few pseudo instructions to
// make debugging easier. These pseudo instructions do not exist on real
// hardware.
//
// TODO: Also consider allowing these pseudo-instructions to be disabled in the
// simulator, so that users can check that the input is a valid native code.
// (This isn't possible in all cases. Printf won't work, for example.)
//
// Each debug pseudo instruction is represented by a HLT instruction. The HLT
// immediate field is used to identify the type of debug pseudo instruction.
enum DebugHltOpcodes {
kUnreachableOpcode = 0xdeb0,
kPrintfOpcode,
kTraceOpcode,
kLogOpcode,
// Aliases.
kDebugHltFirstOpcode = kUnreachableOpcode,
kDebugHltLastOpcode = kLogOpcode
};
// Each pseudo instruction uses a custom encoding for additional arguments, as
// described below.
// Unreachable - kUnreachableOpcode
//
// Instruction which should never be executed. This is used as a guard in parts
// of the code that should not be reachable, such as in data encoded inline in
// the instructions.
// Printf - kPrintfOpcode
// - arg_count: The number of arguments.
// - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
//
// Simulate a call to printf.
//
// Floating-point and integer arguments are passed in separate sets of registers
// in AAPCS64 (even for varargs functions), so it is not possible to determine
// the type of each argument without some information about the values that were
// passed in. This information could be retrieved from the printf format string,
// but the format string is not trivial to parse so we encode the relevant
// information with the HLT instruction.
//
// Also, the following registers are populated (as if for a native A64 call):
// x0: The format string
// x1-x7: Optional arguments, if type == CPURegister::kRegister
// d0-d7: Optional arguments, if type == CPURegister::kFPRegister
const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
const unsigned kPrintfLength = 3 * kInstructionSize;
const unsigned kPrintfMaxArgCount = 4;
// The argument pattern is a set of two-bit-fields, each with one of the
// following values:
enum PrintfArgPattern {
kPrintfArgW = 1,
kPrintfArgX = 2,
// There is no kPrintfArgS because floats are always converted to doubles in C
// varargs calls.
kPrintfArgD = 3
};
static const unsigned kPrintfArgPatternBits = 2;
// Trace - kTraceOpcode
// - parameter: TraceParameter stored as a uint32_t
// - command: TraceCommand stored as a uint32_t
//
// Allow for trace management in the generated code. This enables or disables
// automatic tracing of the specified information for every simulated
// instruction.
const unsigned kTraceParamsOffset = 1 * kInstructionSize;
const unsigned kTraceCommandOffset = 2 * kInstructionSize;
const unsigned kTraceLength = 3 * kInstructionSize;
// Trace parameters.
enum TraceParameters {
LOG_DISASM = 1 << 0, // Log disassembly.
LOG_REGS = 1 << 1, // Log general purpose registers.
LOG_VREGS = 1 << 2, // Log NEON and floating-point registers.
LOG_SYSREGS = 1 << 3, // Log the flags and system registers.
LOG_WRITE = 1 << 4, // Log writes to memory.
LOG_NONE = 0,
LOG_STATE = LOG_REGS | LOG_VREGS | LOG_SYSREGS,
LOG_ALL = LOG_DISASM | LOG_STATE | LOG_WRITE
};
// Trace commands.
enum TraceCommand {
TRACE_ENABLE = 1,
TRACE_DISABLE = 2
};
// Log - kLogOpcode
// - parameter: TraceParameter stored as a uint32_t
//
// Print the specified information once. This mechanism is separate from Trace.
// In particular, _all_ of the specified registers are printed, rather than just
// the registers that the instruction writes.
//
// Any combination of the TraceParameters values can be used, except that
// LOG_DISASM is not supported for Log.
const unsigned kLogParamsOffset = 1 * kInstructionSize;
const unsigned kLogLength = 2 * kInstructionSize;
} // namespace vixl
#endif // VIXL_A64_SIMULATOR_CONSTANTS_A64_H_

Просмотреть файл

@ -213,56 +213,39 @@ void Simulator::set_instruction_stats(bool value) {
}
// Helpers ---------------------------------------------------------------------
int64_t Simulator::AddWithCarry(unsigned reg_size,
bool set_flags,
int64_t src1,
int64_t src2,
int64_t carry_in) {
uint64_t Simulator::AddWithCarry(unsigned reg_size,
bool set_flags,
uint64_t left,
uint64_t right,
int carry_in) {
VIXL_ASSERT((carry_in == 0) || (carry_in == 1));
VIXL_ASSERT((reg_size == kXRegSize) || (reg_size == kWRegSize));
uint64_t u1, u2;
int64_t result;
int64_t signed_sum = src1 + src2 + carry_in;
uint64_t max_uint = (reg_size == kWRegSize) ? kWMaxUInt : kXMaxUInt;
uint64_t reg_mask = (reg_size == kWRegSize) ? kWRegMask : kXRegMask;
uint64_t sign_mask = (reg_size == kWRegSize) ? kWSignMask : kXSignMask;
uint32_t N, Z, C, V;
if (reg_size == kWRegSize) {
u1 = static_cast<uint64_t>(src1) & kWRegMask;
u2 = static_cast<uint64_t>(src2) & kWRegMask;
result = signed_sum & kWRegMask;
// Compute the C flag by comparing the sum to the max unsigned integer.
C = ((kWMaxUInt - u1) < (u2 + carry_in)) ||
((kWMaxUInt - u1 - carry_in) < u2);
// Overflow iff the sign bit is the same for the two inputs and different
// for the result.
int64_t s_src1 = src1 << (kXRegSize - kWRegSize);
int64_t s_src2 = src2 << (kXRegSize - kWRegSize);
int64_t s_result = result << (kXRegSize - kWRegSize);
V = ((s_src1 ^ s_src2) >= 0) && ((s_src1 ^ s_result) < 0);
} else {
u1 = static_cast<uint64_t>(src1);
u2 = static_cast<uint64_t>(src2);
result = signed_sum;
// Compute the C flag by comparing the sum to the max unsigned integer.
C = ((kXMaxUInt - u1) < (u2 + carry_in)) ||
((kXMaxUInt - u1 - carry_in) < u2);
// Overflow iff the sign bit is the same for the two inputs and different
// for the result.
V = ((src1 ^ src2) >= 0) && ((src1 ^ result) < 0);
}
N = CalcNFlag(result, reg_size);
Z = CalcZFlag(result);
left &= reg_mask;
right &= reg_mask;
uint64_t result = (left + right + carry_in) & reg_mask;
if (set_flags) {
nzcv().SetN(N);
nzcv().SetZ(Z);
nzcv().SetC(C);
nzcv().SetV(V);
nzcv().SetN(CalcNFlag(result, reg_size));
nzcv().SetZ(CalcZFlag(result));
// Compute the C flag by comparing the result to the max unsigned integer.
uint64_t max_uint_2op = max_uint - carry_in;
bool C = (left > max_uint_2op) || ((max_uint_2op - left) < right);
nzcv().SetC(C ? 1 : 0);
// Overflow iff the sign bit is the same for the two inputs and different
// for the result.
uint64_t left_sign = left & sign_mask;
uint64_t right_sign = right & sign_mask;
uint64_t result_sign = result & sign_mask;
bool V = (left_sign == right_sign) && (left_sign != result_sign);
nzcv().SetV(V ? 1 : 0);
LogSystemRegister(NZCV);
}
return result;

Просмотреть файл

@ -40,6 +40,7 @@
#include "jit/arm64/vixl/Globals-vixl.h"
#include "jit/arm64/vixl/Instructions-vixl.h"
#include "jit/arm64/vixl/Instrument-vixl.h"
#include "jit/arm64/vixl/Simulator-Constants-vixl.h"
#include "jit/arm64/vixl/Utils-vixl.h"
#include "jit/IonTypes.h"
#include "threading/Mutex.h"
@ -55,120 +56,6 @@
namespace vixl {
// Debug instructions.
//
// VIXL's macro-assembler and simulator support a few pseudo instructions to
// make debugging easier. These pseudo instructions do not exist on real
// hardware.
//
// TODO: Provide controls to prevent the macro assembler from emitting
// pseudo-instructions. This is important for ahead-of-time compilers, where the
// macro assembler is built with USE_SIMULATOR but the code will eventually be
// run on real hardware.
//
// TODO: Also consider allowing these pseudo-instructions to be disabled in the
// simulator, so that users can check that the input is a valid native code.
// (This isn't possible in all cases. Printf won't work, for example.)
//
// Each debug pseudo instruction is represented by a HLT instruction. The HLT
// immediate field is used to identify the type of debug pseudo instruction.
enum DebugHltOpcodes {
kUnreachableOpcode = 0xdeb0,
kPrintfOpcode,
kTraceOpcode,
kLogOpcode,
// Aliases.
kDebugHltFirstOpcode = kUnreachableOpcode,
kDebugHltLastOpcode = kLogOpcode
};
// Each pseudo instruction uses a custom encoding for additional arguments, as
// described below.
// Unreachable - kUnreachableOpcode
//
// Instruction which should never be executed. This is used as a guard in parts
// of the code that should not be reachable, such as in data encoded inline in
// the instructions.
// Printf - kPrintfOpcode
// - arg_count: The number of arguments.
// - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
//
// Simulate a call to printf.
//
// Floating-point and integer arguments are passed in separate sets of registers
// in AAPCS64 (even for varargs functions), so it is not possible to determine
// the type of each argument without some information about the values that were
// passed in. This information could be retrieved from the printf format string,
// but the format string is not trivial to parse so we encode the relevant
// information with the HLT instruction.
//
// Also, the following registers are populated (as if for a native A64 call):
// x0: The format string
// x1-x7: Optional arguments, if type == CPURegister::kRegister
// d0-d7: Optional arguments, if type == CPURegister::kFPRegister
const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
const unsigned kPrintfLength = 3 * kInstructionSize;
const unsigned kPrintfMaxArgCount = 4;
// The argument pattern is a set of two-bit-fields, each with one of the
// following values:
enum PrintfArgPattern {
kPrintfArgW = 1,
kPrintfArgX = 2,
// There is no kPrintfArgS because floats are always converted to doubles in C
// varargs calls.
kPrintfArgD = 3
};
static const unsigned kPrintfArgPatternBits = 2;
// Trace - kTraceOpcode
// - parameter: TraceParameter stored as a uint32_t
// - command: TraceCommand stored as a uint32_t
//
// Allow for trace management in the generated code. This enables or disables
// automatic tracing of the specified information for every simulated
// instruction.
const unsigned kTraceParamsOffset = 1 * kInstructionSize;
const unsigned kTraceCommandOffset = 2 * kInstructionSize;
const unsigned kTraceLength = 3 * kInstructionSize;
// Trace parameters.
enum TraceParameters {
LOG_DISASM = 1 << 0, // Log disassembly.
LOG_REGS = 1 << 1, // Log general purpose registers.
LOG_VREGS = 1 << 2, // Log NEON and floating-point registers.
LOG_SYSREGS = 1 << 3, // Log the flags and system registers.
LOG_WRITE = 1 << 4, // Log writes to memory.
LOG_NONE = 0,
LOG_STATE = LOG_REGS | LOG_VREGS | LOG_SYSREGS,
LOG_ALL = LOG_DISASM | LOG_STATE | LOG_WRITE
};
// Trace commands.
enum TraceCommand {
TRACE_ENABLE = 1,
TRACE_DISABLE = 2
};
// Log - kLogOpcode
// - parameter: TraceParameter stored as a uint32_t
//
// Print the specified information once. This mechanism is separate from Trace.
// In particular, _all_ of the specified registers are printed, rather than just
// the registers that the instruction writes.
//
// Any combination of the TraceParameters values can be used, except that
// LOG_DISASM is not supported for Log.
const unsigned kLogParamsOffset = 1 * kInstructionSize;
const unsigned kLogLength = 2 * kInstructionSize;
// Assemble the specified IEEE-754 components into the target type and apply
// appropriate rounding.
// sign: 0 = positive, 1 = negative
@ -877,9 +764,11 @@ class Simulator : public DecoderVisitor {
// Declare all Visitor functions.
#define DECLARE(A) virtual void Visit##A(const Instruction* instr);
VISITOR_LIST(DECLARE)
VISITOR_LIST_THAT_RETURN(DECLARE)
VISITOR_LIST_THAT_DONT_RETURN(DECLARE)
#undef DECLARE
// Integer register accessors.
// Basic accessor: Read the register as the specified type.
@ -1434,11 +1323,11 @@ class Simulator : public DecoderVisitor {
}
void AddSubHelper(const Instruction* instr, int64_t op2);
int64_t AddWithCarry(unsigned reg_size,
bool set_flags,
int64_t src1,
int64_t src2,
int64_t carry_in = 0);
uint64_t AddWithCarry(unsigned reg_size,
bool set_flags,
uint64_t left,
uint64_t right,
int carry_in = 0);
void LogicalHelper(const Instruction* instr, int64_t op2);
void ConditionalCompareHelper(const Instruction* instr, int64_t op2);
void LoadStoreHelper(const Instruction* instr,
@ -2677,7 +2566,7 @@ class Simulator : public DecoderVisitor {
}
static int CalcZFlag(uint64_t result) {
return result == 0;
return (result == 0) ? 1 : 0;
}
static const uint32_t kConditionFlagsMask = 0xf0000000;

Просмотреть файл

@ -7663,6 +7663,63 @@ class LAsmSelectI64 : public LAsmSelectBase<INT64_PIECES, 2 * INT64_PIECES + 1>
}
};
class LWasmBoundsCheck : public LInstructionHelper<0, 1, 0>
{
public:
LIR_HEADER(WasmBoundsCheck);
explicit LWasmBoundsCheck(const LAllocation& ptr) {
setOperand(0, ptr);
}
MWasmBoundsCheck* mir() const {
return mir_->toWasmBoundsCheck();
}
const LAllocation* ptr() {
return getOperand(0);
}
};
class LWasmLoad : public LInstructionHelper<1, 1, 1>
{
public:
LIR_HEADER(WasmLoad);
explicit LWasmLoad(const LAllocation& ptr) {
setOperand(0, ptr);
setTemp(0, LDefinition::BogusTemp());
}
MWasmLoad* mir() const {
return mir_->toWasmLoad();
}
const LAllocation* ptr() {
return getOperand(0);
}
const LDefinition* ptrCopy() {
return getTemp(0);
}
};
class LWasmStore : public LInstructionHelper<0, 2, 1>
{
public:
LIR_HEADER(WasmStore);
LWasmStore(const LAllocation& ptr, const LAllocation& value) {
setOperand(0, ptr);
setOperand(1, value);
setTemp(0, LDefinition::BogusTemp());
}
MWasmStore* mir() const {
return mir_->toWasmStore();
}
const LAllocation* ptr() {
return getOperand(0);
}
const LDefinition* ptrCopy() {
return getTemp(0);
}
const LAllocation* value() {
return getOperand(1);
}
};
class LAsmJSLoadHeap : public LInstructionHelper<1, 1, 0>
{
public:

Просмотреть файл

@ -384,6 +384,9 @@
_(HasClass) \
_(AsmSelect) \
_(AsmSelectI64) \
_(WasmLoad) \
_(WasmStore) \
_(WasmBoundsCheck) \
_(AsmJSLoadHeap) \
_(AsmJSStoreHeap) \
_(AsmJSLoadFuncPtr) \

Просмотреть файл

@ -628,6 +628,66 @@ AsmJSMemoryAccess(uint32_t before, wasm::MemoryAccess::OutOfBoundsBehavior throw
offsetWithinWholeSimdVector);
}
static wasm::MemoryAccess
WasmMemoryAccess(uint32_t before)
{
return wasm::MemoryAccess(before,
wasm::MemoryAccess::Throw,
wasm::MemoryAccess::DontWrapOffset);
}
void
CodeGeneratorX64::visitWasmLoad(LWasmLoad* ins)
{
const MWasmLoad* mir = ins->mir();
Scalar::Type accessType = mir->accessType();
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
if (mir->offset() > INT32_MAX) {
masm.jump(wasm::JumpTarget::OutOfBounds);
return;
}
const LAllocation* ptr = ins->ptr();
Operand srcAddr = ptr->isBogus()
? Operand(HeapReg, mir->offset())
: Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
AnyRegister out = ToAnyRegister(ins->output());
uint32_t before = masm.size();
load(accessType, srcAddr, out);
masm.append(WasmMemoryAccess(before));
}
void
CodeGeneratorX64::visitWasmStore(LWasmStore* ins)
{
const MWasmStore* mir = ins->mir();
Scalar::Type accessType = mir->accessType();
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
if (mir->offset() > INT32_MAX) {
masm.jump(wasm::JumpTarget::OutOfBounds);
return;
}
const LAllocation* value = ins->value();
const LAllocation* ptr = ins->ptr();
Operand dstAddr = ptr->isBogus()
? Operand(HeapReg, mir->offset())
: Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
uint32_t before = masm.size();
store(accessType, value, dstAddr);
masm.append(WasmMemoryAccess(before));
}
void
CodeGeneratorX64::emitSimdLoad(LAsmJSLoadHeap* ins)
{
@ -683,6 +743,29 @@ CodeGeneratorX64::emitSimdLoad(LAsmJSLoadHeap* ins)
cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
}
void
CodeGeneratorX64::load(Scalar::Type type, const Operand& srcAddr, AnyRegister out)
{
switch (type) {
case Scalar::Int8: masm.movsbl(srcAddr, out.gpr()); break;
case Scalar::Uint8: masm.movzbl(srcAddr, out.gpr()); break;
case Scalar::Int16: masm.movswl(srcAddr, out.gpr()); break;
case Scalar::Uint16: masm.movzwl(srcAddr, out.gpr()); break;
case Scalar::Int32:
case Scalar::Uint32: masm.movl(srcAddr, out.gpr()); break;
case Scalar::Float32: masm.loadFloat32(srcAddr, out.fpu()); break;
case Scalar::Float64: masm.loadDouble(srcAddr, out.fpu()); break;
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4:
MOZ_CRASH("SIMD loads should be handled in emitSimdLoad");
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
}
void
CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
{
@ -704,23 +787,7 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
DebugOnly<bool> hasBoundsCheck = maybeEmitAsmJSLoadBoundsCheck(mir, ins, &ool);
uint32_t before = masm.size();
switch (accessType) {
case Scalar::Int8: masm.movsbl(srcAddr, ToRegister(out)); break;
case Scalar::Uint8: masm.movzbl(srcAddr, ToRegister(out)); break;
case Scalar::Int16: masm.movswl(srcAddr, ToRegister(out)); break;
case Scalar::Uint16: masm.movzwl(srcAddr, ToRegister(out)); break;
case Scalar::Int32:
case Scalar::Uint32: masm.movl(srcAddr, ToRegister(out)); break;
case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break;
case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break;
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4: MOZ_CRASH("SIMD loads should be handled in emitSimdLoad");
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
load(accessType, srcAddr, ToAnyRegister(out));
uint32_t after = masm.size();
verifyHeapAccessDisassembly(before, after, /*isLoad=*/true, accessType, 0, srcAddr, *out->output());
@ -736,6 +803,60 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
masm.append(AsmJSMemoryAccess(before, wasm::MemoryAccess::CarryOn));
}
void
CodeGeneratorX64::store(Scalar::Type type, const LAllocation* value, const Operand& dstAddr)
{
if (value->isConstant()) {
Imm32 cst(ToInt32(value));
switch (type) {
case Scalar::Int8:
case Scalar::Uint8: masm.movb(cst, dstAddr); break;
case Scalar::Int16:
case Scalar::Uint16: masm.movw(cst, dstAddr); break;
case Scalar::Int32:
case Scalar::Uint32: masm.movl(cst, dstAddr); break;
case Scalar::Float32:
case Scalar::Float64:
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4:
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
} else {
switch (type) {
case Scalar::Int8:
case Scalar::Uint8:
masm.movb(ToRegister(value), dstAddr);
break;
case Scalar::Int16:
case Scalar::Uint16:
masm.movw(ToRegister(value), dstAddr);
break;
case Scalar::Int32:
case Scalar::Uint32:
masm.movl(ToRegister(value), dstAddr);
break;
case Scalar::Float32:
masm.storeUncanonicalizedFloat32(ToFloatRegister(value), dstAddr);
break;
case Scalar::Float64:
masm.storeUncanonicalizedDouble(ToFloatRegister(value), dstAddr);
break;
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4:
MOZ_CRASH("SIMD stores must be handled in emitSimdStore");
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
}
}
void
CodeGeneratorX64::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in,
const Operand& dstAddr)
@ -861,54 +982,7 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
DebugOnly<bool> hasBoundsCheck = maybeEmitAsmJSStoreBoundsCheck(mir, ins, &rejoin);
uint32_t before = masm.size();
if (value->isConstant()) {
switch (accessType) {
case Scalar::Int8:
case Scalar::Uint8: masm.movb(Imm32(ToInt32(value)), dstAddr); break;
case Scalar::Int16:
case Scalar::Uint16: masm.movw(Imm32(ToInt32(value)), dstAddr); break;
case Scalar::Int32:
case Scalar::Uint32: masm.movl(Imm32(ToInt32(value)), dstAddr); break;
case Scalar::Float32:
case Scalar::Float64:
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4:
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
} else {
switch (accessType) {
case Scalar::Int8:
case Scalar::Uint8:
masm.movb(ToRegister(value), dstAddr);
break;
case Scalar::Int16:
case Scalar::Uint16:
masm.movw(ToRegister(value), dstAddr);
break;
case Scalar::Int32:
case Scalar::Uint32:
masm.movl(ToRegister(value), dstAddr);
break;
case Scalar::Float32:
masm.storeUncanonicalizedFloat32(ToFloatRegister(value), dstAddr);
break;
case Scalar::Float64:
masm.storeUncanonicalizedDouble(ToFloatRegister(value), dstAddr);
break;
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4:
MOZ_CRASH("SIMD stores must be handled in emitSimdStore");
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
}
store(accessType, value, dstAddr);
uint32_t after = masm.size();
verifyHeapAccessDisassembly(before, after, /*isLoad=*/false, accessType, 0, dstAddr, *value);
@ -924,20 +998,6 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
masm.append(AsmJSMemoryAccess(before, wasm::MemoryAccess::CarryOn));
}
static void
MaybeAddAtomicsBoundsCheck(MacroAssemblerX64& masm, MWasmMemoryAccess* mir, Register ptr)
{
if (!mir->needsBoundsCheck())
return;
// Note that we can't use the same machinery as normal asm.js loads/stores
// since signal-handler bounds checking is not yet implemented for atomic
// accesses.
uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(-mir->endOffset())).offset();
masm.append(wasm::BoundsCheck(cmpOffset));
masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
}
void
CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
{
@ -951,7 +1011,10 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
Register oldval = ToRegister(ins->oldValue());
Register newval = ToRegister(ins->newValue());
MaybeAddAtomicsBoundsCheck(masm, mir, ptr);
// Note that we can't use the same machinery as normal asm.js loads/stores
// since signal-handler bounds checking is not yet implemented for atomic
// accesses.
maybeEmitWasmBoundsCheckBranch(mir, ptr);
masm.compareExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
srcAddr,
@ -977,7 +1040,8 @@ CodeGeneratorX64::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
BaseIndex srcAddr(HeapReg, ptr, TimesOne, mir->offset());
Register value = ToRegister(ins->value());
MaybeAddAtomicsBoundsCheck(masm, mir, ptr);
// See comment in visitAsmJSCompareExchangeHeap.
maybeEmitWasmBoundsCheckBranch(mir, ptr);
masm.atomicExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
srcAddr,
@ -1006,7 +1070,8 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
const LAllocation* value = ins->value();
MaybeAddAtomicsBoundsCheck(masm, mir, ptr);
// See comment in visitAsmJSCompareExchangeHeap.
maybeEmitWasmBoundsCheckBranch(mir, ptr);
AnyRegister output = ToAnyRegister(ins->output());
if (value->isConstant()) {
@ -1036,7 +1101,8 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff
BaseIndex srcAddr(HeapReg, ptr, TimesOne, mir->offset());
const LAllocation* value = ins->value();
MaybeAddAtomicsBoundsCheck(masm, mir, ptr);
// See comment in visitAsmJSCompareExchangeHeap.
maybeEmitWasmBoundsCheckBranch(mir, ptr);
if (value->isConstant())
atomicBinopToTypedIntArray(op, accessType, Imm32(ToInt32(value)), srcAddr);

Просмотреть файл

@ -27,9 +27,13 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
Operand dest, MIRType slotType);
void memoryBarrier(MemoryBarrierBits barrier);
void load(Scalar::Type type, const Operand& srcAddr, AnyRegister out);
void loadSimd(Scalar::Type type, unsigned numElems, const Operand& srcAddr, FloatRegister out);
void emitSimdLoad(LAsmJSLoadHeap* ins);
void store(Scalar::Type type, const LAllocation* value, const Operand& dstAddr);
void storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in, const Operand& dstAddr);
void emitSimdLoad(LAsmJSLoadHeap* ins);
void emitSimdStore(LAsmJSStoreHeap* ins);
public:
CodeGeneratorX64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm);
@ -64,6 +68,8 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
void visitWasmLoad(LWasmLoad* ins);
void visitWasmStore(LWasmStore* ins);
void visitAsmSelectI64(LAsmSelectI64* ins);
void visitAsmJSCall(LAsmJSCall* ins);
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);

Просмотреть файл

@ -155,6 +155,40 @@ LIRGeneratorX64::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
define(lir, ins);
}
void
LIRGeneratorX64::visitWasmStore(MWasmStore* ins)
{
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
LAllocation value;
switch (ins->accessType()) {
case Scalar::Int8:
case Scalar::Uint8:
case Scalar::Int16:
case Scalar::Uint16:
case Scalar::Int32:
case Scalar::Uint32:
value = useRegisterOrConstantAtStart(ins->value());
break;
case Scalar::Float32:
case Scalar::Float64:
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4:
value = useRegisterAtStart(ins->value());
break;
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
LAllocation baseAlloc = useRegisterOrZeroAtStart(base);
auto* lir = new(alloc()) LWasmStore(baseAlloc, value);
add(lir, ins);
}
void
LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{

Просмотреть файл

@ -56,6 +56,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
void visitWasmStore(MWasmStore* ins);
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);

Просмотреть файл

@ -1077,18 +1077,31 @@ class AssemblerX86Shared : public AssemblerShared
}
static void UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength) {
// An access is out-of-bounds iff
// ptr + offset + data-type-byte-size > heapLength
// i.e. ptr > heapLength - data-type-byte-size - offset.
// data-type-byte-size and offset are already included in the addend so
// we just have to add the heap length here.
//
// On x64, even with signal handling being used for most bounds checks,
// there may be atomic operations that depend on explicit checks. All
// accesses that have been recorded are the only ones that need bound
// checks (see also
// CodeGeneratorX64::visitAsmJS{Load,Store,CompareExchange,Exchange,AtomicBinop}Heap)
X86Encoding::AddInt32(patchAt, heapLength);
// checks.
//
// An access is out-of-bounds iff
// ptr + offset + data-type-byte-size > heapLength
// i.e ptr + offset + data-type-byte-size - 1 >= heapLength
// i.e. ptr >= heapLength - data-type-byte-size - offset + 1.
//
// before := data-type-byte-size + offset - 1
uint32_t before = reinterpret_cast<uint32_t*>(patchAt)[-1];
uint32_t after = before + heapLength;
// If the computed index `before` already is out of bounds,
// we need to make sure the bounds check will fail all the time.
// For bounds checks, the sequence of instructions we use is:
// cmp(ptrReg, #before)
// jae(OutOfBounds)
// so replace the cmp immediate with 0.
if (after > heapLength)
after = 0;
MOZ_ASSERT_IF(after, int32_t(after) >= int32_t(before));
reinterpret_cast<uint32_t*>(patchAt)[-1] = after;
}
void breakpoint() {

Просмотреть файл

@ -454,11 +454,14 @@ CodeGeneratorX86Shared::emitAsmJSBoundsCheckBranch(const MWasmMemoryAccess* acce
// field, so -access->endOffset() will turn into
// (heapLength - access->endOffset()), allowing us to test whether the end
// of the access is beyond the end of the heap.
uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(-access->endOffset())).offset();
MOZ_ASSERT(access->endOffset() >= 1,
"need to subtract 1 to use JAE, see also AssemblerX86Shared::UpdateBoundsCheck");
uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(1 - access->endOffset())).offset();
if (maybeFail)
masm.j(Assembler::Above, maybeFail);
masm.j(Assembler::AboveOrEqual, maybeFail);
else
masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
masm.j(Assembler::AboveOrEqual, wasm::JumpTarget::OutOfBounds);
if (pass)
masm.bind(pass);
@ -466,6 +469,34 @@ CodeGeneratorX86Shared::emitAsmJSBoundsCheckBranch(const MWasmMemoryAccess* acce
masm.append(wasm::BoundsCheck(cmpOffset));
}
void
CodeGeneratorX86Shared::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
{
const MWasmBoundsCheck* mir = ins->mir();
MOZ_ASSERT(gen->needsBoundsCheckBranch(mir));
if (mir->offset() > INT32_MAX) {
masm.jump(wasm::JumpTarget::OutOfBounds);
return;
}
Register ptrReg = ToRegister(ins->ptr());
maybeEmitWasmBoundsCheckBranch(mir, ptrReg);
}
void
CodeGeneratorX86Shared::maybeEmitWasmBoundsCheckBranch(const MWasmMemoryAccess* mir, Register ptr)
{
if (!mir->needsBoundsCheck())
return;
MOZ_ASSERT(mir->endOffset() >= 1,
"need to subtract 1 to use JAE, see also AssemblerX86Shared::UpdateBoundsCheck");
uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(1 - mir->endOffset())).offset();
masm.j(Assembler::AboveOrEqual, wasm::JumpTarget::OutOfBounds);
masm.append(wasm::BoundsCheck(cmpOffset));
}
bool
CodeGeneratorX86Shared::maybeEmitThrowingAsmJSBoundsCheck(const MWasmMemoryAccess* access,
const MInstruction* mir,

Просмотреть файл

@ -94,24 +94,24 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
};
private:
void
emitAsmJSBoundsCheckBranch(const MWasmMemoryAccess* mir, const MInstruction* ins,
Register ptr, Label* fail);
void emitAsmJSBoundsCheckBranch(const MWasmMemoryAccess* mir, const MInstruction* ins,
Register ptr, Label* fail);
protected:
void maybeEmitWasmBoundsCheckBranch(const MWasmMemoryAccess* mir, Register ptr);
public:
// For SIMD and atomic loads and stores (which throw on out-of-bounds):
bool
maybeEmitThrowingAsmJSBoundsCheck(const MWasmMemoryAccess* mir, const MInstruction* ins,
const LAllocation* ptr);
bool maybeEmitThrowingAsmJSBoundsCheck(const MWasmMemoryAccess* mir, const MInstruction* ins,
const LAllocation* ptr);
// For asm.js plain and atomic loads that possibly require a bounds check:
bool
maybeEmitAsmJSLoadBoundsCheck(const MAsmJSLoadHeap* mir, LAsmJSLoadHeap* ins,
OutOfLineLoadTypedArrayOutOfBounds** ool);
bool maybeEmitAsmJSLoadBoundsCheck(const MAsmJSLoadHeap* mir, LAsmJSLoadHeap* ins,
OutOfLineLoadTypedArrayOutOfBounds** ool);
// For asm.js plain and atomic stores that possibly require a bounds check:
bool
maybeEmitAsmJSStoreBoundsCheck(const MAsmJSStoreHeap* mir, LAsmJSStoreHeap* ins, Label** rejoin);
bool maybeEmitAsmJSStoreBoundsCheck(const MAsmJSStoreHeap* mir, LAsmJSStoreHeap* ins,
Label** rejoin);
void cleanupAfterAsmJSBoundsCheckBranch(const MWasmMemoryAccess* mir, Register ptr);
@ -272,6 +272,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
virtual void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
virtual void visitAsmSelect(LAsmSelect* ins);
virtual void visitAsmReinterpret(LAsmReinterpret* lir);
virtual void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
virtual void visitMemoryBarrier(LMemoryBarrier* ins);
virtual void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
virtual void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir);

Просмотреть файл

@ -322,6 +322,27 @@ LIRGeneratorX86Shared::visitAsmJSNeg(MAsmJSNeg* ins)
}
}
void
LIRGeneratorX86Shared::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
{
if (!gen->needsBoundsCheckBranch(ins))
return;
MDefinition* index = ins->input();
auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(index));
add(lir, ins);
}
void
LIRGeneratorX86Shared::visitWasmLoad(MWasmLoad* ins)
{
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
auto* lir = new(alloc()) LWasmLoad(useRegisterOrZeroAtStart(base));
define(lir, ins);
}
void
LIRGeneratorX86Shared::lowerUDiv(MDiv* div)
{

Просмотреть файл

@ -47,6 +47,8 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared
void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
MDefinition* lhs, MDefinition* rhs);
void visitAsmJSNeg(MAsmJSNeg* ins);
void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
void visitWasmLoad(MWasmLoad* ins);
void visitAsmSelect(MAsmSelect* ins);
void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
void lowerDivI(MDiv* div);

Просмотреть файл

@ -42,17 +42,6 @@ SetInt32(void* where, int32_t value)
reinterpret_cast<int32_t*>(where)[-1] = value;
}
inline void
AddInt32(void* where, int32_t value)
{
#ifdef DEBUG
uint32_t x = reinterpret_cast<uint32_t*>(where)[-1];
uint32_t y = x + uint32_t(value);
MOZ_ASSERT(value >= 0 ? (int32_t(y) >= int32_t(x)) : (int32_t(y) < int32_t(x)));
#endif
reinterpret_cast<uint32_t*>(where)[-1] += uint32_t(value);
}
inline void
SetRel32(void* from, void* to)
{

Просмотреть файл

@ -394,6 +394,57 @@ CodeGeneratorX86::loadSimd(Scalar::Type type, unsigned numElems, const Operand&
}
}
void
CodeGeneratorX86::visitWasmLoad(LWasmLoad* ins)
{
const MWasmLoad* mir = ins->mir();
Scalar::Type accessType = mir->accessType();
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
if (mir->offset() > INT32_MAX) {
// This is unreachable because of the bounds check.
masm.breakpoint();
return;
}
const LAllocation* ptr = ins->ptr();
Operand srcAddr = ptr->isBogus()
? Operand(PatchedAbsoluteAddress(mir->offset()))
: Operand(ToRegister(ptr), mir->offset());
load(accessType, srcAddr, ins->output());
masm.append(wasm::MemoryAccess(masm.size()));
}
void
CodeGeneratorX86::visitWasmStore(LWasmStore* ins)
{
const MWasmStore* mir = ins->mir();
Scalar::Type accessType = mir->accessType();
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
if (mir->offset() > INT32_MAX) {
// This is unreachable because of the bounds check.
masm.breakpoint();
return;
}
const LAllocation* value = ins->value();
const LAllocation* ptr = ins->ptr();
Operand dstAddr = ptr->isBogus()
? Operand(PatchedAbsoluteAddress(mir->offset()))
: Operand(ToRegister(ptr), mir->offset());
store(accessType, value, dstAddr);
masm.append(wasm::MemoryAccess(masm.size()));
}
void
CodeGeneratorX86::emitSimdLoad(LAsmJSLoadHeap* ins)
{
@ -664,6 +715,23 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
masm.append(wasm::MemoryAccess(after));
}
// Perform bounds checking on the access if necessary; if it fails,
// jump to out-of-line code that throws. If the bounds check passes,
// set up the heap address in addrTemp.
void
CodeGeneratorX86::asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg,
const MWasmMemoryAccess* mir)
{
maybeEmitWasmBoundsCheckBranch(mir, ptrReg);
// Add in the actual heap pointer explicitly, to avoid opening up
// the abstraction that is atomicBinopToTypedIntArray at this time.
masm.movl(ptrReg, addrTemp);
masm.addlWithPatch(Imm32(mir->offset()), addrTemp);
masm.append(wasm::MemoryAccess(masm.size()));
}
void
CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
{
@ -674,8 +742,7 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
Register newval = ToRegister(ins->newValue());
Register addrTemp = ToRegister(ins->addrTemp());
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
mir->endOffset());
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir);
Address memAddr(addrTemp, mir->offset());
masm.compareExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
@ -686,27 +753,6 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
ToAnyRegister(ins->output()));
}
// Perform bounds checking on the access if necessary; if it fails,
// jump to out-of-line code that throws. If the bounds check passes,
// set up the heap address in addrTemp.
void
CodeGeneratorX86::asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
uint32_t offset, uint32_t endOffset)
{
if (boundsCheck) {
uint32_t cmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-endOffset)).offset();
masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
masm.append(wasm::BoundsCheck(cmpOffset));
}
// Add in the actual heap pointer explicitly, to avoid opening up
// the abstraction that is atomicBinopToTypedIntArray at this time.
masm.movl(ptrReg, addrTemp);
masm.addlWithPatch(Imm32(offset), addrTemp);
masm.append(wasm::MemoryAccess(masm.size()));
}
void
CodeGeneratorX86::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
{
@ -716,8 +762,7 @@ CodeGeneratorX86::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
Register value = ToRegister(ins->value());
Register addrTemp = ToRegister(ins->addrTemp());
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
mir->endOffset());
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir);
Address memAddr(addrTemp, mir->offset());
masm.atomicExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
@ -738,8 +783,7 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
const LAllocation* value = ins->value();
AtomicOp op = mir->operation();
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
mir->endOffset());
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir);
Address memAddr(addrTemp, mir->offset());
if (value->isConstant()) {
@ -771,8 +815,7 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff
MOZ_ASSERT(!mir->hasUses());
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir->needsBoundsCheck(), mir->offset(),
mir->endOffset());
asmJSAtomicComputeAddress(addrTemp, ptrReg, mir);
Address memAddr(addrTemp, mir->offset());
if (value->isConstant())

Просмотреть файл

@ -58,6 +58,8 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
void visitAsmJSCall(LAsmJSCall* ins);
void visitWasmLoad(LWasmLoad* ins);
void visitWasmStore(LWasmStore* ins);
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
@ -74,8 +76,8 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
void visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool);
private:
void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
uint32_t offset, uint32_t endOffset);
void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg,
const MWasmMemoryAccess* access);
};
typedef CodeGeneratorX86 CodeGeneratorSpecific;

Просмотреть файл

@ -203,6 +203,40 @@ LIRGeneratorX86::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
define(lir, ins);
}
void
LIRGeneratorX86::visitWasmStore(MWasmStore* ins)
{
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
LAllocation baseAlloc = useRegisterOrZeroAtStart(base);
LAllocation valueAlloc;
switch (ins->accessType()) {
case Scalar::Int8: case Scalar::Uint8:
// See comment for LIRGeneratorX86::useByteOpRegister.
valueAlloc = useFixed(ins->value(), eax);
break;
case Scalar::Int16: case Scalar::Uint16:
case Scalar::Int32: case Scalar::Uint32:
case Scalar::Float32: case Scalar::Float64:
case Scalar::Float32x4:
case Scalar::Int8x16:
case Scalar::Int16x8:
case Scalar::Int32x4:
// For now, don't allow constant values. The immediate operand affects
// instruction layout which affects patching.
valueAlloc = useRegisterAtStart(ins->value());
break;
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
MOZ_CRASH("unexpected array type");
}
auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc);
add(lir, ins);
}
void
LIRGeneratorX86::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{

Просмотреть файл

@ -60,6 +60,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
void visitWasmStore(MWasmStore* ins);
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);

Просмотреть файл

@ -462,7 +462,7 @@ ArrayBufferObject::createForWasm(JSContext* cx, uint32_t numBytes, bool signalsF
#endif
}
auto buffer = ArrayBufferObject::create(cx, numBytes);
auto* buffer = ArrayBufferObject::create(cx, numBytes);
if (!buffer)
return nullptr;
@ -633,6 +633,10 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
size_t nAllocated = nbytes;
if (contents.kind() == MAPPED)
nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
else if (contents.kind() == WASM_MAPPED)
nAllocated = wasm::MappedSize;
#endif
cx->zone()->updateMallocCounter(nAllocated);
}
} else {

Просмотреть файл

@ -1358,6 +1358,28 @@ BaseShape::traceShapeTable(JSTracer* trc)
table().trace(trc);
}
#ifdef DEBUG
bool
BaseShape::canSkipMarkingShapeTable(Shape* lastShape)
{
// Check that every shape in the shape table will be marked by marking
// |lastShape|.
if (!hasTable())
return true;
uint32_t count = 0;
for (Shape::Range<NoGC> r(lastShape); !r.empty(); r.popFront()) {
Shape* shape = &r.front();
ShapeTable::Entry& entry = table().search<MaybeAdding::NotAdding>(shape->propid());
if (entry.isLive())
count++;
}
return count == table().entryCount();
}
#endif
#ifdef JSGC_HASH_TABLE_CHECKS
void

Просмотреть файл

@ -129,6 +129,7 @@ enum class MaybeAdding { Adding = true, NotAdding = false };
class ShapeTable {
public:
friend class NativeObject;
friend class BaseShape;
static const uint32_t MIN_ENTRIES = 11;
class Entry {
@ -145,6 +146,7 @@ class ShapeTable {
public:
bool isFree() const { return shape_ == nullptr; }
bool isRemoved() const { return shape_ == SHAPE_REMOVED; }
bool isLive() const { return !isFree() && !isRemoved(); }
bool hadCollision() const { return uintptr_t(shape_) & SHAPE_COLLISION; }
void setFree() { shape_ = nullptr; }
@ -454,6 +456,10 @@ class BaseShape : public gc::TenuredCell
void traceChildren(JSTracer* trc);
void traceChildrenSkipShapeTable(JSTracer* trc);
#ifdef DEBUG
bool canSkipMarkingShapeTable(Shape* lastShape);
#endif
private:
static void staticAsserts() {
JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_));

Просмотреть файл

@ -5484,7 +5484,7 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
{
NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
NS_PRECONDITION(aFrame->IsTransformed() ||
aFrame->StyleDisplay()->BackfaceIsHidden() ||
aFrame->BackfaceIsHidden() ||
aFrame->Combines3DTransformWithAncestors(),
"Shouldn't get a delta for an untransformed frame!");
@ -5557,7 +5557,7 @@ nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
{
NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
NS_PRECONDITION(aFrame->IsTransformed() ||
aFrame->StyleDisplay()->BackfaceIsHidden() ||
aFrame->BackfaceIsHidden() ||
aFrame->Combines3DTransformWithAncestors(),
"Shouldn't get a delta for an untransformed frame!");
NS_PRECONDITION(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
@ -5891,8 +5891,7 @@ static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix)
if (aMatrix.IsSingular()) {
return false;
}
if (aFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
aMatrix.IsBackfaceVisible()) {
if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
return false;
}
return true;

Просмотреть файл

@ -1854,7 +1854,7 @@ public:
const DisplayItemScrollClip* ScrollClip() const { return mScrollClip; }
bool BackfaceIsHidden() {
return mFrame->StyleDisplay()->BackfaceIsHidden();
return mFrame->BackfaceIsHidden();
}
protected:

Просмотреть файл

@ -454,7 +454,7 @@ BackgroundClipTextEnabledPrefChangeCallback(const char* aPrefName,
template<typename TestType>
static bool
HasMatchingCurrentAnimations(const nsIFrame* aFrame, TestType&& aTest)
HasMatchingAnimations(const nsIFrame* aFrame, TestType&& aTest)
{
EffectSet* effects = EffectSet::GetEffectSet(aFrame);
if (!effects) {
@ -462,10 +462,6 @@ HasMatchingCurrentAnimations(const nsIFrame* aFrame, TestType&& aTest)
}
for (KeyframeEffectReadOnly* effect : *effects) {
if (!effect->IsCurrent()) {
continue;
}
if (aTest(*effect)) {
return true;
}
@ -478,10 +474,10 @@ bool
nsLayoutUtils::HasCurrentAnimationOfProperty(const nsIFrame* aFrame,
nsCSSProperty aProperty)
{
return HasMatchingCurrentAnimations(aFrame,
return HasMatchingAnimations(aFrame,
[&aProperty](KeyframeEffectReadOnly& aEffect)
{
return aEffect.HasAnimationOfProperty(aProperty);
return aEffect.IsCurrent() && aEffect.HasAnimationOfProperty(aProperty);
}
);
}
@ -489,12 +485,12 @@ nsLayoutUtils::HasCurrentAnimationOfProperty(const nsIFrame* aFrame,
bool
nsLayoutUtils::HasCurrentTransitions(const nsIFrame* aFrame)
{
return HasMatchingCurrentAnimations(aFrame,
return HasMatchingAnimations(aFrame,
[](KeyframeEffectReadOnly& aEffect)
{
// Since |aEffect| is current, it must have an associated Animation
// so we don't need to null-check the result of GetAnimation().
return aEffect.GetAnimation()->AsCSSTransition();
return aEffect.IsCurrent() && aEffect.GetAnimation()->AsCSSTransition();
}
);
}
@ -504,10 +500,24 @@ nsLayoutUtils::HasCurrentAnimationsForProperties(const nsIFrame* aFrame,
const nsCSSProperty* aProperties,
size_t aPropertyCount)
{
return HasMatchingCurrentAnimations(aFrame,
return HasMatchingAnimations(aFrame,
[&aProperties, &aPropertyCount](KeyframeEffectReadOnly& aEffect)
{
return aEffect.HasAnimationOfProperties(aProperties, aPropertyCount);
return aEffect.IsCurrent() &&
aEffect.HasAnimationOfProperties(aProperties, aPropertyCount);
}
);
}
bool
nsLayoutUtils::HasRelevantAnimationOfProperty(const nsIFrame* aFrame,
nsCSSProperty aProperty)
{
return HasMatchingAnimations(aFrame,
[&aProperty](KeyframeEffectReadOnly& aEffect)
{
return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
aEffect.HasAnimationOfProperty(aProperty);
}
);
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше