зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
944f87c575
|
@ -240,6 +240,10 @@ function chromeFileExists(aURI) {
|
|||
}
|
||||
|
||||
add_task(function* checkAllTheCSS() {
|
||||
// Since we later in this test use Services.console.getMessageArray(),
|
||||
// better to not have some messages from previous tests in the array.
|
||||
Services.console.reset();
|
||||
|
||||
let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
|
||||
// This asynchronously produces a list of URLs (sadly, mostly sync on our
|
||||
// test infrastructure because it runs against jarfiles there, and
|
||||
|
|
|
@ -9,7 +9,7 @@ add_task(function* setup() {
|
|||
/**
|
||||
* For loading the initial about:blank in e10s mode, it will be loaded with
|
||||
* NullPrincipal, and we also test if the firstPartyDomain of the origin
|
||||
* attributes is NULL_PRINCIPAL_FIRST_PARTY_DOMAIN.
|
||||
* attributes is got from the origin itself.
|
||||
*/
|
||||
add_task(function* test_remote_window_open_aboutBlank() {
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow({ remote: true });
|
||||
|
@ -17,15 +17,17 @@ add_task(function* test_remote_window_open_aboutBlank() {
|
|||
|
||||
Assert.ok(browser.isRemoteBrowser, "should be a remote browser");
|
||||
|
||||
let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
|
||||
yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
|
||||
yield ContentTask.spawn(browser, {}, function* () {
|
||||
info("origin " + content.document.nodePrincipal.origin);
|
||||
|
||||
Assert.ok(content.document.nodePrincipal.isNullPrincipal,
|
||||
"The principal of remote about:blank should be a NullPrincipal.");
|
||||
|
||||
let str = content.document.nodePrincipal.originNoSuffix;
|
||||
let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
|
||||
Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
expectAttrs.firstPartyDomain,
|
||||
"remote about:blank should have firstPartyDomain set");
|
||||
expectDomain,
|
||||
"remote about:blank should have firstPartyDomain set to " + expectDomain);
|
||||
});
|
||||
|
||||
win.close();
|
||||
|
@ -75,15 +77,17 @@ add_task(function* test_remote_window_open_data_uri() {
|
|||
return url == "data:text/plain,hello";
|
||||
});
|
||||
|
||||
let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
|
||||
yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
|
||||
yield ContentTask.spawn(browser, {}, function* () {
|
||||
info("origin: " + content.document.nodePrincipal.origin);
|
||||
|
||||
Assert.ok(content.document.nodePrincipal.isNullPrincipal,
|
||||
"The principal of data: document should be a NullPrincipal.");
|
||||
|
||||
let str = content.document.nodePrincipal.originNoSuffix;
|
||||
let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
|
||||
Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
expectAttrs.firstPartyDomain,
|
||||
"data: URI should have firstPartyDomain set");
|
||||
expectDomain,
|
||||
"data: URI should have firstPartyDomain set to " + expectDomain);
|
||||
});
|
||||
|
||||
win.close();
|
||||
|
@ -103,8 +107,7 @@ add_task(function* test_remote_window_open_data_uri2() {
|
|||
browser.loadURI(DATA_URI);
|
||||
yield BrowserTestUtils.browserLoaded(browser, true);
|
||||
|
||||
let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
|
||||
yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
|
||||
yield ContentTask.spawn(browser, {}, function* () {
|
||||
info("origin " + content.document.nodePrincipal.origin);
|
||||
|
||||
let iframe = content.document.getElementById("iframe1");
|
||||
|
@ -112,12 +115,15 @@ add_task(function* test_remote_window_open_data_uri2() {
|
|||
|
||||
Assert.ok(content.document.nodePrincipal.isNullPrincipal,
|
||||
"The principal of data: document should be a NullPrincipal.");
|
||||
|
||||
let str = content.document.nodePrincipal.originNoSuffix;
|
||||
let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
|
||||
Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
expectAttrs.firstPartyDomain,
|
||||
"data: URI should have firstPartyDomain set");
|
||||
expectDomain,
|
||||
"data: URI should have firstPartyDomain set to " + expectDomain);
|
||||
|
||||
Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
expectAttrs.firstPartyDomain,
|
||||
expectDomain,
|
||||
"iframe should inherit firstPartyDomain from parent document.");
|
||||
Assert.equal(iframe.contentDocument.cookie, "test2=foo", "iframe should have cookies");
|
||||
});
|
||||
|
|
|
@ -13,15 +13,17 @@ add_task(function* test_remote_window_open_js_uri() {
|
|||
Assert.ok(browser.isRemoteBrowser, "should be a remote browser");
|
||||
|
||||
browser.loadURI(`javascript:1;`);
|
||||
let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
|
||||
yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
|
||||
yield ContentTask.spawn(browser, {}, function* () {
|
||||
info("origin " + content.document.nodePrincipal.origin);
|
||||
|
||||
Assert.ok(content.document.nodePrincipal.isNullPrincipal,
|
||||
"The principal of remote javascript: should be a NullPrincipal.");
|
||||
|
||||
let str = content.document.nodePrincipal.originNoSuffix;
|
||||
let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
|
||||
Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
expectAttrs.firstPartyDomain,
|
||||
"remote javascript: should have firstPartyDomain set");
|
||||
expectDomain,
|
||||
"remote javascript: should have firstPartyDomain set to " + expectDomain);
|
||||
});
|
||||
|
||||
win.close();
|
||||
|
@ -46,18 +48,22 @@ add_task(function* test_remote_window_open_js_uri2() {
|
|||
return url == "http://example.com/";
|
||||
});
|
||||
|
||||
let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
|
||||
yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
|
||||
yield ContentTask.spawn(browser, {}, function* () {
|
||||
info("origin " + content.document.nodePrincipal.origin);
|
||||
|
||||
Assert.ok(content.document.nodePrincipal.isNullPrincipal,
|
||||
"The principal of remote javascript: should be a NullPrincipal.");
|
||||
|
||||
let str = content.document.nodePrincipal.originNoSuffix;
|
||||
let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
|
||||
Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
expectAttrs.firstPartyDomain,
|
||||
"remote javascript: should have firstPartyDomain set");
|
||||
expectDomain,
|
||||
"remote javascript: should have firstPartyDomain set to " + expectDomain);
|
||||
|
||||
let iframe = content.document.getElementById("iframe1");
|
||||
info("iframe principal: " + iframe.contentDocument.nodePrincipal.origin);
|
||||
Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
expectDomain,
|
||||
"iframe should have firstPartyDomain set to " + expectDomain);
|
||||
});
|
||||
|
||||
win.close();
|
||||
|
|
|
@ -82,7 +82,7 @@ fi
|
|||
|
||||
AC_SUBST(MOZ_NO_DEBUG_RTL)
|
||||
|
||||
MOZ_DEBUG_ENABLE_DEFS="DEBUG TRACING"
|
||||
MOZ_DEBUG_ENABLE_DEFS="DEBUG"
|
||||
MOZ_ARG_WITH_STRING(debug-label,
|
||||
[ --with-debug-label=LABELS
|
||||
Define DEBUG_<value> for each comma-separated
|
||||
|
|
|
@ -49,10 +49,9 @@ NullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom)
|
|||
NullPrincipal::CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty)
|
||||
{
|
||||
OriginAttributes attrs = nsDocShell::Cast(aDocShell)->GetOriginAttributes();
|
||||
attrs.SetFirstPartyDomain(aIsFirstParty, NS_LITERAL_CSTRING(NULL_PRINCIPAL_FIRST_PARTY_DOMAIN));
|
||||
|
||||
RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
|
||||
nsresult rv = nullPrin->Init(attrs);
|
||||
nsresult rv = nullPrin->Init(attrs, aIsFirstParty);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
return nullPrin.forget();
|
||||
}
|
||||
|
@ -93,6 +92,33 @@ NullPrincipal::Init(const OriginAttributes& aOriginAttributes, nsIURI* aURI)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullPrincipal::Init(const OriginAttributes& aOriginAttributes, bool aIsFirstParty)
|
||||
{
|
||||
mURI = NullPrincipalURI::Create();
|
||||
NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsAutoCString originNoSuffix;
|
||||
DebugOnly<nsresult> rv = mURI->GetSpec(originNoSuffix);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
nsAutoCString path;
|
||||
rv = mURI->GetPath(path);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
OriginAttributes attrs(aOriginAttributes);
|
||||
if (aIsFirstParty) {
|
||||
// remove the '{}' characters from both ends.
|
||||
path.Mid(path, 1, path.Length() - 2);
|
||||
path.AppendLiteral(".mozilla");
|
||||
attrs.SetFirstPartyDomain(true, path);
|
||||
}
|
||||
|
||||
FinishInit(originNoSuffix, attrs);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullPrincipal::GetScriptLocation(nsACString &aStr)
|
||||
{
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
// Create NullPrincipal with origin attributes from docshell.
|
||||
// If aIsFirstParty is true, and the pref 'privacy.firstparty.isolate' is also
|
||||
// enabled, the mFirstPartyDomain value of the origin attributes will be set
|
||||
// to NULL_PRINCIPAL_FIRST_PARTY_DOMAIN.
|
||||
// to an unique value.
|
||||
static already_AddRefed<NullPrincipal>
|
||||
CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty = false);
|
||||
|
||||
|
@ -81,6 +81,12 @@ public:
|
|||
bool MayLoadInternal(nsIURI* aURI) override;
|
||||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
|
||||
private:
|
||||
// If aIsFirstParty is true, this NullPrincipal will be initialized base on
|
||||
// the aOriginAttributes with FirstPartyDomain set to an unique value, and this
|
||||
// value is generated from mURI.path, with ".mozilla" appending at the end.
|
||||
nsresult Init(const mozilla::OriginAttributes& aOriginAttributes, bool aIsFirstParty);
|
||||
};
|
||||
|
||||
#endif // NullPrincipal_h__
|
||||
|
|
|
@ -3271,7 +3271,7 @@ nsRange::AutoInvalidateSelection::~AutoInvalidateSelection()
|
|||
mIsNested = false;
|
||||
::InvalidateAllFrames(mCommonAncestor);
|
||||
nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor();
|
||||
if (commonAncestor != mCommonAncestor) {
|
||||
if (commonAncestor && commonAncestor != mCommonAncestor) {
|
||||
::InvalidateAllFrames(commonAncestor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
try { o1 = document.createElement('tr'); } catch(e) {};
|
||||
try { o2 = document.createElement('div'); } catch(e) {};
|
||||
try { o3 = document.createElement('hr'); } catch(e) {};
|
||||
try { o4 = document.createElement('textarea'); } catch(e) {};
|
||||
try { o5 = document.getSelection(); } catch(e) {};
|
||||
try { o6 = document.createRange(); } catch(e) {};
|
||||
try { document.documentElement.appendChild(o2); } catch(e) {};
|
||||
try { document.documentElement.appendChild(o3); } catch(e) {};
|
||||
try { o2.appendChild(o4); } catch(e) {};
|
||||
try { o3.outerHTML = "<noscript contenteditable='true'>"; } catch(e) {};
|
||||
try { o4.select(); } catch(e) {};
|
||||
try { o5.addRange(o6); } catch(e) {};
|
||||
try { document.documentElement.appendChild(o1); } catch(e) {};
|
||||
try { o5.selectAllChildren(o1); } catch(e) {};
|
||||
try { o6.selectNode(o1); } catch(e) {};
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
|
@ -81,3 +81,4 @@ load 1290904.html
|
|||
load 1343886-1.html
|
||||
load 1343886-2.xml
|
||||
load 1343886-3.xml
|
||||
asserts(0-3) load 1350972.html
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsINetworkInterceptController.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
#include "nsIUnicodeEncoder.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
|
@ -108,6 +109,12 @@ NS_IMETHODIMP
|
|||
CancelChannelRunnable::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// TODO: When bug 1204254 is implemented, this time marker should be moved to
|
||||
// the point where the body of the network request is complete.
|
||||
mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
|
||||
mChannel->SaveTimeStampsToUnderlyingChannel();
|
||||
|
||||
mChannel->Cancel(mStatus);
|
||||
mRegistration->MaybeScheduleUpdate();
|
||||
return NS_OK;
|
||||
|
@ -230,6 +237,9 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
|
||||
mChannel->SaveTimeStampsToUnderlyingChannel();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
||||
if (obsService) {
|
||||
obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "nsINetworkInterceptController.h"
|
||||
#include "nsIPushErrorReporter.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsIUploadChannel2.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
@ -1297,6 +1298,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable
|
|||
nsCString mMethod;
|
||||
nsString mClientId;
|
||||
bool mIsReload;
|
||||
bool mMarkLaunchServiceWorkerEnd;
|
||||
RequestCache mCacheMode;
|
||||
RequestMode mRequestMode;
|
||||
RequestRedirect mRequestRedirect;
|
||||
|
@ -1315,13 +1317,15 @@ public:
|
|||
const nsACString& aScriptSpec,
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
|
||||
const nsAString& aDocumentId,
|
||||
bool aIsReload)
|
||||
bool aIsReload,
|
||||
bool aMarkLaunchServiceWorkerEnd)
|
||||
: ExtendableFunctionalEventWorkerRunnable(
|
||||
aWorkerPrivate, aKeepAliveToken, aRegistration)
|
||||
, mInterceptedChannel(aChannel)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mClientId(aDocumentId)
|
||||
, mIsReload(aIsReload)
|
||||
, mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd)
|
||||
, mCacheMode(RequestCache::Default)
|
||||
, mRequestMode(RequestMode::No_cors)
|
||||
, mRequestRedirect(RequestRedirect::Follow)
|
||||
|
@ -1474,6 +1478,12 @@ public:
|
|||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
if (mMarkLaunchServiceWorkerEnd) {
|
||||
mInterceptedChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
|
||||
}
|
||||
|
||||
mInterceptedChannel->SetDispatchFetchEventEnd(TimeStamp::Now());
|
||||
return DispatchFetchEvent(aCx, aWorkerPrivate);
|
||||
}
|
||||
|
||||
|
@ -1502,6 +1512,10 @@ private:
|
|||
NS_IMETHOD Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
|
||||
mChannel->SaveTimeStampsToUnderlyingChannel();
|
||||
|
||||
nsresult rv = mChannel->ResetInterception();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Failed to resume intercepted network request");
|
||||
|
@ -1577,6 +1591,8 @@ private:
|
|||
event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec);
|
||||
event->SetTrusted(true);
|
||||
|
||||
mInterceptedChannel->SetHandleFetchEventStart(TimeStamp::Now());
|
||||
|
||||
nsresult rv2 =
|
||||
DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
|
||||
event, nullptr);
|
||||
|
@ -1647,9 +1663,20 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
|
|||
nsCOMPtr<nsIRunnable> failRunnable =
|
||||
NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
|
||||
|
||||
nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup);
|
||||
aChannel->SetLaunchServiceWorkerStart(TimeStamp::Now());
|
||||
aChannel->SetDispatchFetchEventStart(TimeStamp::Now());
|
||||
|
||||
bool newWorkerCreated = false;
|
||||
nsresult rv = SpawnWorkerIfNeeded(FetchEvent,
|
||||
failRunnable,
|
||||
&newWorkerCreated,
|
||||
aLoadGroup);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!newWorkerCreated) {
|
||||
aChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
|
||||
new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
|
||||
|
||||
|
@ -1658,10 +1685,11 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
|
|||
|
||||
RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
|
||||
|
||||
|
||||
RefPtr<FetchEventRunnable> r =
|
||||
new FetchEventRunnable(mWorkerPrivate, token, handle,
|
||||
mInfo->ScriptSpec(), regInfo,
|
||||
aDocumentId, aIsReload);
|
||||
aDocumentId, aIsReload, newWorkerCreated);
|
||||
rv = r->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
|
@ -1684,6 +1712,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
|
|||
nsresult
|
||||
ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
|
||||
nsIRunnable* aLoadFailedRunnable,
|
||||
bool* aNewWorkerCreated,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
@ -1694,6 +1723,12 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
|
|||
// the overriden load group when intercepting a fetch.
|
||||
MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup);
|
||||
|
||||
// Defaults to no new worker created, but if there is one, we'll set the value
|
||||
// to true at the end of this function.
|
||||
if (aNewWorkerCreated) {
|
||||
*aNewWorkerCreated = false;
|
||||
}
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
|
||||
RenewKeepAliveToken(aWhy);
|
||||
|
@ -1800,6 +1835,10 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
|
|||
|
||||
RenewKeepAliveToken(aWhy);
|
||||
|
||||
if (aNewWorkerCreated) {
|
||||
*aNewWorkerCreated = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -196,6 +196,7 @@ private:
|
|||
nsresult
|
||||
SpawnWorkerIfNeeded(WakeUpReason aWhy,
|
||||
nsIRunnable* aLoadFailedRunnable,
|
||||
bool* aNewWorkerCreated = nullptr,
|
||||
nsILoadGroup* aLoadGroup = nullptr);
|
||||
|
||||
~ServiceWorkerPrivate();
|
||||
|
|
|
@ -3,6 +3,8 @@ skip-if = os == 'android'
|
|||
support-files =
|
||||
chrome_helpers.js
|
||||
empty.js
|
||||
fetch.js
|
||||
hello.html
|
||||
serviceworker.html
|
||||
serviceworkerinfo_iframe.html
|
||||
serviceworkermanager_iframe.html
|
||||
|
@ -10,6 +12,7 @@ support-files =
|
|||
worker.js
|
||||
worker2.js
|
||||
|
||||
[test_devtools_serviceworker_interception.html]
|
||||
[test_privateBrowsing.html]
|
||||
[test_serviceworkerinfo.xul]
|
||||
[test_serviceworkermanager.xul]
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1168875 - test devtools serviceworker interception.</title>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet"
|
||||
type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// Constants
|
||||
const Ci = Components.interfaces;
|
||||
const workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/";
|
||||
const workerURL = workerScope + "fetch.js";
|
||||
const contentPage = workerScope + "hello.html";
|
||||
|
||||
function createTestWindow(aURL) {
|
||||
var mainwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
var win = mainwindow.OpenBrowserWindow(contentPage);
|
||||
|
||||
return new Promise(aResolve => {
|
||||
win.addEventListener("DOMContentLoaded", function callback() {
|
||||
if (win.content.location.href != aURL) {
|
||||
win.gBrowser.loadURI(aURL);
|
||||
return;
|
||||
}
|
||||
|
||||
win.removeEventListener("DOMContentLoaded", callback);
|
||||
aResolve(win.content);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function executeTest(aWindow) {
|
||||
var registration;
|
||||
|
||||
return Promise.resolve()
|
||||
// Should not be intercepted.
|
||||
.then(_ => fetchAndCheckTimedChannel(aWindow, false, true, "hello.html"))
|
||||
|
||||
// Regist a service worker.
|
||||
.then(_ => register(aWindow, workerURL, workerScope))
|
||||
.then(r => registration = r)
|
||||
|
||||
// Should be intercpeted and synthesized.
|
||||
.then(_ => fetchAndCheckTimedChannel(aWindow, true, false, "fake.html"))
|
||||
|
||||
// Should be intercepted but still fetch from network.
|
||||
.then(_ => fetchAndCheckTimedChannel(aWindow, true, true,
|
||||
"hello.html?ForBypassingHttpCache"))
|
||||
|
||||
// Tear down
|
||||
.then(_ => registration.unregister());
|
||||
}
|
||||
|
||||
function register(aWindow, aURL, aScope) {
|
||||
return aWindow.navigator.serviceWorker.register(aURL, {scope: aScope})
|
||||
.then(r => {
|
||||
var worker = r.installing;
|
||||
return new Promise(function(aResolve) {
|
||||
worker.onstatechange = function() {
|
||||
if (worker.state == "activated") {
|
||||
aResolve(r);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fetchAndCheckTimedChannel(aWindow, aIntercepted, aFetch, aURL) {
|
||||
var resolveFunction;
|
||||
var promise = new Promise(aResolve => resolveFunction = aResolve);
|
||||
|
||||
var topic = aFetch ? "http-on-examine-response"
|
||||
: "service-worker-synthesized-response";
|
||||
|
||||
function observer(aSubject) {
|
||||
var channel = aSubject.QueryInterface(Ci.nsIChannel);
|
||||
ok(channel.URI.spec.endsWith(aURL));
|
||||
|
||||
var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
|
||||
|
||||
// Check service worker related timings.
|
||||
var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
|
||||
end: tc.launchServiceWorkerEndTime},
|
||||
{start: tc.dispatchFetchEventStartTime,
|
||||
end: tc.dispatchFetchEventEndTime},
|
||||
{start: tc.handleFetchEventStartTime,
|
||||
end: tc.handleFetchEventEndTime}];
|
||||
if (aIntercepted) {
|
||||
serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
|
||||
ok(aPreviousTimings.start <= aCurrentTimings.start,
|
||||
"Start time order check.");
|
||||
ok(aPreviousTimings.end <= aCurrentTimings.end,
|
||||
"End time order check.");
|
||||
ok(aCurrentTimings.start <= aCurrentTimings.end,
|
||||
"Start time should be smaller than end time.");
|
||||
return aCurrentTimings;
|
||||
});
|
||||
} else {
|
||||
serviceWorkerTimings.forEach(aTimings => {
|
||||
is(aTimings.start, 0);
|
||||
is(aTimings.end, 0);
|
||||
});
|
||||
}
|
||||
|
||||
// Check network related timings.
|
||||
var networkTimings = [tc.domainLookupStartTime,
|
||||
tc.domainLookupEndTime,
|
||||
tc.connectStartTime,
|
||||
tc.connectEndTime,
|
||||
tc.requestStartTime,
|
||||
tc.responseStartTime,
|
||||
tc.responseEndTime];
|
||||
if (aFetch) {
|
||||
networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
|
||||
ok(aPreviousTiming <= aCurrentTiming);
|
||||
return aCurrentTiming;
|
||||
});
|
||||
} else {
|
||||
networkTimings.forEach(aTiming => is(aTiming, 0));
|
||||
}
|
||||
|
||||
SpecialPowers.removeObserver(observer, topic);
|
||||
resolveFunction();
|
||||
}
|
||||
|
||||
SpecialPowers.addObserver(observer, topic, false);
|
||||
|
||||
// return promise;
|
||||
return Promise.all([aWindow.fetch(aURL), promise]);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
return Promise.resolve()
|
||||
.then(_ => createTestWindow(contentPage))
|
||||
.then(w => executeTest(w))
|
||||
.catch(e => ok(false, "Some test failed with error " + e))
|
||||
.then(_ => SimpleTest.finish());
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -78,40 +78,5 @@ DrawEventRecorderFile::Close()
|
|||
mOutputFile.close();
|
||||
}
|
||||
|
||||
DrawEventRecorderMemory::DrawEventRecorderMemory()
|
||||
: DrawEventRecorderPrivate(nullptr)
|
||||
{
|
||||
mOutputStream = &mMemoryStream;
|
||||
|
||||
WriteHeader();
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderMemory::Flush()
|
||||
{
|
||||
mOutputStream->flush();
|
||||
}
|
||||
|
||||
size_t
|
||||
DrawEventRecorderMemory::RecordingSize()
|
||||
{
|
||||
return mMemoryStream.tellp();
|
||||
}
|
||||
|
||||
bool
|
||||
DrawEventRecorderMemory::CopyRecording(char* aBuffer, size_t aBufferLen)
|
||||
{
|
||||
return !!mMemoryStream.read(aBuffer, aBufferLen);
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderMemory::WipeRecording()
|
||||
{
|
||||
mMemoryStream.str(std::string());
|
||||
mMemoryStream.clear();
|
||||
|
||||
WriteHeader();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -103,53 +103,6 @@ private:
|
|||
std::ofstream mOutputFile;
|
||||
};
|
||||
|
||||
class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory)
|
||||
|
||||
/**
|
||||
* Constructs a DrawEventRecorder that stores the recording in memory.
|
||||
*/
|
||||
DrawEventRecorderMemory();
|
||||
|
||||
/**
|
||||
* @return the current size of the recording (in chars).
|
||||
*/
|
||||
size_t RecordingSize();
|
||||
|
||||
/**
|
||||
* Copies at most aBufferLen chars of the recording into aBuffer.
|
||||
*
|
||||
* @param aBuffer buffer to receive the recording chars
|
||||
* @param aBufferLen length of aBuffer
|
||||
* @return true if copied successfully
|
||||
*/
|
||||
bool CopyRecording(char* aBuffer, size_t aBufferLen);
|
||||
|
||||
/**
|
||||
* Wipes the internal recording buffer, but the recorder does NOT forget which
|
||||
* objects it has recorded. This can be used so that a recording can be copied
|
||||
* and processed in chunks, releasing memory as it goes.
|
||||
*/
|
||||
void WipeRecording();
|
||||
|
||||
/**
|
||||
* Gets a readable reference of the underlying stream, reset to the beginning.
|
||||
*/
|
||||
std::istream& GetInputStream() {
|
||||
mMemoryStream.seekg(0);
|
||||
return mMemoryStream;
|
||||
}
|
||||
|
||||
private:
|
||||
~DrawEventRecorderMemory() {};
|
||||
|
||||
void Flush() final;
|
||||
|
||||
std::stringstream mMemoryStream;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -30,6 +30,16 @@ interface imgICache : nsISupports
|
|||
*/
|
||||
void clearCache(in boolean chrome);
|
||||
|
||||
/**
|
||||
* Evict images from the cache.
|
||||
*
|
||||
* @param uri The URI to remove.
|
||||
* @param doc The document to remove the cache entry for.
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if \a uri was unable to be removed from
|
||||
* the cache.
|
||||
*/
|
||||
[noscript] void removeEntry(in nsIURI uri, [optional] in nsIDOMDocument doc);
|
||||
|
||||
/**
|
||||
* Find Properties
|
||||
* Used to get properties such as 'type' and 'content-disposition'
|
||||
|
|
|
@ -1369,6 +1369,29 @@ imgLoader::ClearCache(bool chrome)
|
|||
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
imgLoader::RemoveEntry(nsIURI* aURI,
|
||||
nsIDOMDocument* aDOMDoc)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDoc);
|
||||
if (aURI) {
|
||||
OriginAttributes attrs;
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
|
||||
if (principal) {
|
||||
attrs = principal->OriginAttributesRef();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
ImageCacheKey key(aURI, attrs, doc, rv);
|
||||
if (NS_SUCCEEDED(rv) && RemoveFromCache(key)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
imgLoader::FindEntryProperties(nsIURI* uri,
|
||||
nsIDOMDocument* aDOMDoc,
|
||||
|
@ -2169,6 +2192,18 @@ imgLoader::LoadImage(nsIURI* aURI,
|
|||
request->SetCacheEntry(entry);
|
||||
|
||||
if (mCacheTracker) {
|
||||
if (MOZ_UNLIKELY(!entry->GetExpirationState()->IsTracked())) {
|
||||
bool inCache = false;
|
||||
RefPtr<imgCacheEntry> e;
|
||||
if (cache.Get(key, getter_AddRefs(e)) && e) {
|
||||
inCache = (e == entry);
|
||||
}
|
||||
gfxCriticalNoteOnce << "entry with no proxies is no in tracker "
|
||||
<< "request->HasConsumers() "
|
||||
<< (request->HasConsumers() ? "true" : "false")
|
||||
<< " inCache " << (inCache ? "true" : "false");
|
||||
}
|
||||
|
||||
mCacheTracker->MarkUsed(entry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2414,16 +2414,16 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
|
|||
// For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20.
|
||||
|
||||
// Steps 7-8.
|
||||
RootedValue value(cx);
|
||||
if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
|
||||
return AbruptRejectPromise(cx, args, resultPromise, nullptr);
|
||||
|
||||
// Steps 9-10.
|
||||
RootedValue doneVal(cx);
|
||||
if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
|
||||
return AbruptRejectPromise(cx, args, resultPromise, nullptr);
|
||||
bool done = ToBoolean(doneVal);
|
||||
|
||||
// Steps 9-10.
|
||||
RootedValue value(cx);
|
||||
if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
|
||||
return AbruptRejectPromise(cx, args, resultPromise, nullptr);
|
||||
|
||||
// Step 11.
|
||||
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
|
||||
if (!promise)
|
||||
|
|
|
@ -244,9 +244,9 @@ class LoopControl : public BreakableControl
|
|||
loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1;
|
||||
|
||||
int loopSlots;
|
||||
if (loopKind == StatementKind::Spread || loopKind == StatementKind::ForOfLoop)
|
||||
if (loopKind == StatementKind::Spread)
|
||||
loopSlots = 3;
|
||||
else if (loopKind == StatementKind::ForInLoop)
|
||||
else if (loopKind == StatementKind::ForInLoop || loopKind == StatementKind::ForOfLoop)
|
||||
loopSlots = 2;
|
||||
else
|
||||
loopSlots = 0;
|
||||
|
@ -269,6 +269,20 @@ class LoopControl : public BreakableControl
|
|||
return canIonOsr_;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool emitSpecialBreakForDone(BytecodeEmitter* bce) {
|
||||
// This doesn't pop stack values, nor handle any other controls.
|
||||
// Should be called on the toplevel of the loop.
|
||||
MOZ_ASSERT(bce->stackDepth == stackDepth_);
|
||||
MOZ_ASSERT(bce->innermostNestableControl == this);
|
||||
|
||||
if (!bce->newSrcNote(SRC_BREAK))
|
||||
return false;
|
||||
if (!bce->emitJump(JSOP_GOTO, &breaks))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool patchBreaksAndContinues(BytecodeEmitter* bce) {
|
||||
MOZ_ASSERT(continueTarget.offset != -1);
|
||||
if (!patchBreaks(bce))
|
||||
|
@ -2080,11 +2094,11 @@ class ForOfLoopControl : public LoopControl
|
|||
}
|
||||
|
||||
bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) {
|
||||
// Pop unnecessary values from the stack. Effectively this means
|
||||
// Pop unnecessary value from the stack. Effectively this means
|
||||
// leaving try-catch block. However, the performing IteratorClose can
|
||||
// reach the depth for try-catch, and effectively re-enter the
|
||||
// try-catch block.
|
||||
if (!bce->emitPopN(2)) // ITER
|
||||
if (!bce->emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
|
||||
// Clear ITER slot on the stack to tell catch block to avoid performing
|
||||
|
@ -2100,11 +2114,9 @@ class ForOfLoopControl : public LoopControl
|
|||
if (isTarget) {
|
||||
// At the level of the target block, there's bytecode after the
|
||||
// loop that will pop the iterator and the value, so push
|
||||
// undefineds to balance the stack.
|
||||
// an undefined to balance the stack.
|
||||
if (!bce->emit1(JSOP_UNDEFINED)) // UNDEF UNDEF
|
||||
return false;
|
||||
if (!bce->emit1(JSOP_UNDEFINED)) // UNDEF UNDEF UNDEF
|
||||
return false;
|
||||
} else {
|
||||
if (!bce->emit1(JSOP_POP)) //
|
||||
return false;
|
||||
|
@ -2780,7 +2792,7 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta
|
|||
if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ...
|
||||
return false;
|
||||
} else {
|
||||
npops += 3;
|
||||
npops += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2805,7 +2817,7 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta
|
|||
|
||||
if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
|
||||
ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
|
||||
if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF
|
||||
if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -7026,11 +7038,9 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
|
|||
|
||||
int32_t iterDepth = stackDepth;
|
||||
|
||||
// For-of loops have both the iterator, the result, and the result.value
|
||||
// on the stack. Push undefineds to balance the stack.
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT UNDEF
|
||||
// For-of loops have both the iterator and the result.value on the stack.
|
||||
// Push an undefined to balance the stack.
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind);
|
||||
|
@ -7041,11 +7051,11 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
|
|||
return false;
|
||||
|
||||
JumpList initialJump;
|
||||
if (!emitJump(JSOP_GOTO, &initialJump)) // ITER RESULT UNDEF
|
||||
if (!emitJump(JSOP_GOTO, &initialJump)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
JumpTarget top{ -1 };
|
||||
if (!emitLoopHead(nullptr, &top)) // ITER RESULT UNDEF
|
||||
if (!emitLoopHead(nullptr, &top)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// If the loop had an escaping lexical declaration, replace the current
|
||||
|
@ -7062,7 +7072,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
|
|||
MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical);
|
||||
|
||||
if (headLexicalEmitterScope->hasEnvironment()) {
|
||||
if (!emit1(JSOP_RECREATELEXICALENV)) // ITER RESULT UNDEF
|
||||
if (!emit1(JSOP_RECREATELEXICALENV)) // ITER UNDEF
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -7078,21 +7088,49 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
|
|||
auto loopDepth = this->stackDepth;
|
||||
#endif
|
||||
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP)) // ITER ITER
|
||||
return false;
|
||||
|
||||
if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter))
|
||||
return false; // ITER RESULT
|
||||
|
||||
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE
|
||||
return false;
|
||||
|
||||
IfThenElseEmitter ifDone(this);
|
||||
|
||||
if (!ifDone.emitIf()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Remove RESULT from the stack to release it.
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// If the iteration is done, leave loop here, instead of the branch at
|
||||
// the end of the loop.
|
||||
if (!loopInfo.emitSpecialBreakForDone(this)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
if (!ifDone.emitEnd()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Emit code to assign result.value to the iteration variable.
|
||||
//
|
||||
// Note that ES 13.7.5.13, step 5.c says getting result.value does not
|
||||
// call IteratorClose, so start JSTRY_ITERCLOSE after the GETPROP.
|
||||
if (!emit1(JSOP_POP)) // ITER RESULT
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER RESULT VALUE
|
||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER VALUE
|
||||
return false;
|
||||
|
||||
if (!loopInfo.emitBeginCodeNeedingIteratorClose(this))
|
||||
return false;
|
||||
|
||||
if (!emitInitializeForInOrOfTarget(forOfHead)) // ITER RESULT VALUE
|
||||
if (!emitInitializeForInOrOfTarget(forOfHead)) // ITER VALUE
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(stackDepth == loopDepth,
|
||||
|
@ -7100,14 +7138,14 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
|
|||
"operation");
|
||||
|
||||
// Remove VALUE from the stack to release it.
|
||||
if (!emit1(JSOP_POP)) // ITER RESULT
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT UNDEF
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// Perform the loop body.
|
||||
ParseNode* forBody = forOfLoop->pn_right;
|
||||
if (!emitTree(forBody)) // ITER RESULT UNDEF
|
||||
if (!emitTree(forBody)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(stackDepth == loopDepth,
|
||||
|
@ -7119,29 +7157,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
|
|||
// Set offset for continues.
|
||||
loopInfo.continueTarget = { offset() };
|
||||
|
||||
if (!emitLoopEntry(forHeadExpr, initialJump)) // ITER RESULT UNDEF
|
||||
if (!emitLoopEntry(forHeadExpr, initialJump)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_SWAP)) // ITER UNDEF RESULT
|
||||
if (!emit1(JSOP_FALSE)) // ITER UNDEF FALSE
|
||||
return false;
|
||||
if (!emit1(JSOP_POP)) // ITER UNDEF
|
||||
return false;
|
||||
if (!emitDupAt(1)) // ITER UNDEF ITER
|
||||
return false;
|
||||
|
||||
if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) // ITER UNDEF RESULT
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_SWAP)) // ITER RESULT UNDEF
|
||||
return false;
|
||||
|
||||
if (!emitDupAt(1)) // ITER RESULT UNDEF RESULT
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT UNDEF DONE
|
||||
return false;
|
||||
|
||||
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
|
||||
return false; // ITER RESULT UNDEF
|
||||
return false; // ITER UNDEF
|
||||
|
||||
MOZ_ASSERT(this->stackDepth == loopDepth);
|
||||
}
|
||||
|
@ -7156,7 +7178,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
|
|||
if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset))
|
||||
return false;
|
||||
|
||||
return emitPopN(3); //
|
||||
return emitPopN(2); //
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -7566,9 +7588,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
|
|||
return false;
|
||||
|
||||
// Push a dummy result so that we properly enter iteration midstream.
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT VALUE
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER VALUE
|
||||
return false;
|
||||
|
||||
// Enter the block before the loop body, after evaluating the obj.
|
||||
|
@ -7606,31 +7626,56 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
|
|||
int loopDepth = this->stackDepth;
|
||||
#endif
|
||||
|
||||
// Emit code to assign result.value to the iteration variable.
|
||||
if (!emit1(JSOP_POP)) // ITER RESULT
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP)) // ITER ITER
|
||||
return false;
|
||||
if (!emitIteratorNext(forHead)) // ITER RESULT
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER RESULT VALUE
|
||||
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE
|
||||
return false;
|
||||
|
||||
IfThenElseEmitter ifDone(this);
|
||||
|
||||
if (!ifDone.emitIf()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Remove RESULT from the stack to release it.
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// If the iteration is done, leave loop here, instead of the branch at
|
||||
// the end of the loop.
|
||||
if (!loopInfo.emitSpecialBreakForDone(this)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
if (!ifDone.emitEnd()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Emit code to assign result.value to the iteration variable.
|
||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER VALUE
|
||||
return false;
|
||||
|
||||
// Notice: Comprehension for-of doesn't perform IteratorClose, since it's
|
||||
// not in the spec.
|
||||
|
||||
if (!emitAssignment(loopVariableName, JSOP_NOP, nullptr)) // ITER RESULT VALUE
|
||||
if (!emitAssignment(loopVariableName, JSOP_NOP, nullptr)) // ITER VALUE
|
||||
return false;
|
||||
|
||||
// Remove VALUE from the stack to release it.
|
||||
if (!emit1(JSOP_POP)) // ITER RESULT
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT UNDEF
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// The stack should be balanced around the assignment opcode sequence.
|
||||
MOZ_ASSERT(this->stackDepth == loopDepth);
|
||||
|
||||
// Emit code for the loop body.
|
||||
if (!emitTree(forBody)) // ITER RESULT UNDEF
|
||||
if (!emitTree(forBody)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// The stack should be balanced around the assignment opcode sequence.
|
||||
|
@ -7642,25 +7687,13 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
|
|||
if (!emitLoopEntry(forHeadExpr, jmp))
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_SWAP)) // ITER UNDEF RESULT
|
||||
return false;
|
||||
if (!emit1(JSOP_POP)) // ITER UNDEF
|
||||
return false;
|
||||
if (!emitDupAt(1)) // ITER UNDEF ITER
|
||||
return false;
|
||||
if (!emitIteratorNext(forHead)) // ITER UNDEF RESULT
|
||||
return false;
|
||||
if (!emit1(JSOP_SWAP)) // ITER RESULT UNDEF
|
||||
return false;
|
||||
if (!emitDupAt(1)) // ITER RESULT UNDEF RESULT
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT UNDEF DONE
|
||||
if (!emit1(JSOP_FALSE)) // ITER VALUE FALSE
|
||||
return false;
|
||||
|
||||
JumpList beq;
|
||||
JumpTarget breakTarget{ -1 };
|
||||
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER RESULT UNDEF
|
||||
return false;
|
||||
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
|
||||
return false; // ITER VALUE
|
||||
|
||||
MOZ_ASSERT(this->stackDepth == loopDepth);
|
||||
|
||||
|
@ -7681,7 +7714,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
|
|||
}
|
||||
|
||||
// Pop the result and the iter.
|
||||
return emitPopN(3); //
|
||||
return emitPopN(2); //
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -5973,11 +5973,6 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
|
|||
if (!matchInOrOf(&isForIn, &isForOf))
|
||||
return false;
|
||||
|
||||
if (iterKind == IteratorKind::Async && !isForOf) {
|
||||
error(JSMSG_FOR_AWAIT_NOT_OF);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled
|
||||
// the init expression; the caller handles the rest. Allow the Operand
|
||||
// modifier when regetting: Operand must be used to examine the ';' in
|
||||
|
@ -6125,15 +6120,19 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
|
||||
MOZ_ASSERT(headKind == PNK_FORIN || headKind == PNK_FOROF || headKind == PNK_FORHEAD);
|
||||
|
||||
if (iterKind == IteratorKind::Async && headKind != PNK_FOROF) {
|
||||
errorAt(begin, JSMSG_FOR_AWAIT_NOT_OF);
|
||||
return null();
|
||||
}
|
||||
if (isForEach && headKind != PNK_FORIN) {
|
||||
errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return null();
|
||||
}
|
||||
|
||||
Node forHead;
|
||||
if (headKind == PNK_FORHEAD) {
|
||||
Node init = startNode;
|
||||
|
||||
if (isForEach) {
|
||||
errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Look for an operand: |for (;| means we might have already examined
|
||||
// this semicolon with that modifier.
|
||||
MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
|
||||
|
@ -6189,11 +6188,6 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
stmt.refineForKind(StatementKind::ForInLoop);
|
||||
iflags |= JSITER_ENUMERATE;
|
||||
} else {
|
||||
if (isForEach) {
|
||||
errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return null();
|
||||
}
|
||||
|
||||
stmt.refineForKind(StatementKind::ForOfLoop);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
if (typeof TypedObject === 'undefined')
|
||||
quit();
|
||||
|
||||
var uint8 = TypedObject.uint8;
|
||||
function check(v) {
|
||||
return v.toSource();
|
||||
}
|
||||
function test() {
|
||||
var fake1 = {};
|
||||
var fake2 = [];
|
||||
fake2.toSource = uint8;
|
||||
var a = [fake1, fake2];
|
||||
for (var i = 0; i < 1000; i++) try {
|
||||
check(a[i % 2]);
|
||||
} catch (e) {}
|
||||
}
|
||||
test();
|
|
@ -0,0 +1,132 @@
|
|||
let values = [-Infinity, Infinity, NaN, 0, -0, -0.1, 0.1, 0.5, -1.6, 1.6, 13.37];
|
||||
|
||||
function unary(name) {
|
||||
print(name);
|
||||
|
||||
let imports = {
|
||||
math: {
|
||||
func: Math[name],
|
||||
}
|
||||
};
|
||||
|
||||
let f32 = x => Math.fround(Math[name](Math.fround(x)));
|
||||
let f64 = Math[name];
|
||||
|
||||
let i = wasmEvalText(`(module
|
||||
(import $f32 "math" "func" (param f32) (result f32))
|
||||
(import $f64 "math" "func" (param f64) (result f64))
|
||||
|
||||
(table $t 10 anyfunc)
|
||||
(type $f_f (func (param f32) (result f32)))
|
||||
(type $d_d (func (param f64) (result f64)))
|
||||
(elem (i32.const 0) $f32 $f64)
|
||||
|
||||
(func (export "f32") (param f32) (result f32)
|
||||
get_local 0
|
||||
call $f32
|
||||
)
|
||||
(func (export "f32_t") (param f32) (result f32)
|
||||
get_local 0
|
||||
i32.const 0
|
||||
call_indirect $f_f
|
||||
)
|
||||
(func (export "f64") (param f64) (result f64)
|
||||
get_local 0
|
||||
call $f64
|
||||
)
|
||||
(func (export "f64_t") (param f64) (result f64)
|
||||
get_local 0
|
||||
i32.const 1
|
||||
call_indirect $d_d
|
||||
)
|
||||
)`, imports).exports;
|
||||
|
||||
for (let v of values) {
|
||||
assertEq(i.f32(v), f32(v));
|
||||
assertEq(i.f32_t(v), f32(v));
|
||||
assertEq(i.f64(v), f64(v));
|
||||
assertEq(i.f64_t(v), f64(v));
|
||||
}
|
||||
}
|
||||
|
||||
function binary(name) {
|
||||
print(name);
|
||||
|
||||
let imports = {
|
||||
math: {
|
||||
func: Math[name]
|
||||
}
|
||||
};
|
||||
|
||||
let f32 = (x, y) => Math.fround(Math[name](Math.fround(x), Math.fround(y)));
|
||||
let f64 = Math[name];
|
||||
|
||||
let i = wasmEvalText(`(module
|
||||
(import $f32 "math" "func" (param f32) (param f32) (result f32))
|
||||
(import $f64 "math" "func" (param f64) (param f64) (result f64))
|
||||
|
||||
(table $t 10 anyfunc)
|
||||
(type $ff_f (func (param f32) (param f32) (result f32)))
|
||||
(type $dd_d (func (param f64) (param f64) (result f64)))
|
||||
(elem (i32.const 0) $f32 $f64)
|
||||
|
||||
(func (export "f32") (param f32) (param f32) (result f32)
|
||||
get_local 0
|
||||
get_local 1
|
||||
call $f32
|
||||
)
|
||||
(func (export "f32_t") (param f32) (param f32) (result f32)
|
||||
get_local 0
|
||||
get_local 1
|
||||
i32.const 0
|
||||
call_indirect $ff_f
|
||||
)
|
||||
(func (export "f64") (param f64) (param f64) (result f64)
|
||||
get_local 0
|
||||
get_local 1
|
||||
call $f64
|
||||
)
|
||||
(func (export "f64_t") (param f64) (param f64) (result f64)
|
||||
get_local 0
|
||||
get_local 1
|
||||
i32.const 1
|
||||
call_indirect $dd_d
|
||||
)
|
||||
)`, imports).exports;
|
||||
|
||||
for (let v of values) {
|
||||
for (let w of values) {
|
||||
assertEq(i.f32(v, w), f32(v, w));
|
||||
assertEq(i.f64(v, w), f64(v, w));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unary('sin');
|
||||
unary('sin');
|
||||
unary('tan');
|
||||
unary('cos');
|
||||
unary('exp');
|
||||
unary('log');
|
||||
unary('asin');
|
||||
unary('atan');
|
||||
unary('acos');
|
||||
unary('log10');
|
||||
unary('log2');
|
||||
unary('log1p');
|
||||
unary('expm1');
|
||||
unary('sinh');
|
||||
unary('tanh');
|
||||
unary('cosh');
|
||||
unary('asinh');
|
||||
unary('atanh');
|
||||
unary('acosh');
|
||||
unary('sign');
|
||||
unary('trunc');
|
||||
unary('cbrt');
|
||||
|
||||
binary('atan2');
|
||||
binary('hypot');
|
||||
binary('pow');
|
||||
|
||||
print('done');
|
|
@ -13,10 +13,10 @@ const Table = WebAssembly.Table;
|
|||
function normalize(stack)
|
||||
{
|
||||
var wasmFrameTypes = [
|
||||
{re:/^entry trampoline \(in wasm\)$/, sub:">"},
|
||||
{re:/^wasm-function\[(\d+)\] \(.*\)$/, sub:"$1"},
|
||||
{re:/^(fast|slow) FFI trampoline \(in wasm\)$/, sub:"<"},
|
||||
{re:/ \(in wasm\)$/, sub:""}
|
||||
{re:/^entry trampoline \(in wasm\)$/, sub:">"},
|
||||
{re:/^wasm-function\[(\d+)\] \(.*\)$/, sub:"$1"},
|
||||
{re:/^(fast|slow) FFI trampoline (to native |)\(in wasm\)$/, sub:"<"},
|
||||
{re:/ \(in wasm\)$/, sub:""}
|
||||
];
|
||||
|
||||
var framesIn = stack.split(',');
|
||||
|
@ -113,6 +113,16 @@ test(
|
|||
{"":{foo:()=>{}}},
|
||||
["", ">", "1,>", "0,1,>", "<,0,1,>", "0,1,>", "1,>", ">", ""]);
|
||||
|
||||
test(`(module
|
||||
(import $f32 "Math" "sin" (param f32) (result f32))
|
||||
(func (export "") (param f32) (result f32)
|
||||
get_local 0
|
||||
call $f32
|
||||
)
|
||||
)`,
|
||||
this,
|
||||
["", ">", "1,>", "<,1,>", "1,>", ">", ""]);
|
||||
|
||||
function testError(code, error, expect)
|
||||
{
|
||||
enableGeckoProfiling();
|
||||
|
|
|
@ -516,10 +516,9 @@ HasLiveStackValueAtDepth(JSScript* script, jsbytecode* pc, uint32_t stackDepth)
|
|||
break;
|
||||
|
||||
case JSTRY_FOR_OF:
|
||||
// For-of loops have the iterator, the result object, and the value
|
||||
// of the result object on stack. The iterator is below the result
|
||||
// object and the value.
|
||||
if (stackDepth == tn->stackDepth - 2)
|
||||
// For-of loops have the iterator and the result.value on stack.
|
||||
// The iterator is below the result.value.
|
||||
if (stackDepth == tn->stackDepth - 1)
|
||||
return true;
|
||||
break;
|
||||
|
||||
|
|
|
@ -937,7 +937,7 @@ ControlFlowGenerator::processWhileOrForInLoop(jssrcnote* sn)
|
|||
|
||||
size_t stackPhiCount;
|
||||
if (SN_TYPE(sn) == SRC_FOR_OF)
|
||||
stackPhiCount = 3;
|
||||
stackPhiCount = 2;
|
||||
else if (SN_TYPE(sn) == SRC_FOR_IN)
|
||||
stackPhiCount = 1;
|
||||
else
|
||||
|
|
|
@ -769,11 +769,11 @@ PropertyNameToExtraName(PropertyName* name)
|
|||
|
||||
#endif // DEBUG
|
||||
|
||||
enum {
|
||||
enum ABIArgType {
|
||||
ArgType_General = 0x1,
|
||||
ArgType_Double = 0x2,
|
||||
ArgType_Float32 = 0x3,
|
||||
ArgType_Int64 = 0x4,
|
||||
ArgType_Int64 = 0x4,
|
||||
|
||||
RetType_Shift = 0x0,
|
||||
ArgType_Shift = 0x3,
|
||||
|
@ -828,6 +828,9 @@ enum ABIFunctionType
|
|||
// double f(double, double)
|
||||
Args_Double_DoubleDouble = Args_Double_Double | (ArgType_Double << (ArgType_Shift * 2)),
|
||||
|
||||
// float f(float, float)
|
||||
Args_Float32_Float32Float32 = Args_Float32_Float32 | (ArgType_Float32 << (ArgType_Shift * 2)),
|
||||
|
||||
// double f(int, double)
|
||||
Args_Double_IntDouble = Args_Double_None |
|
||||
(ArgType_Double << (ArgType_Shift * 1)) |
|
||||
|
@ -856,7 +859,6 @@ enum ABIFunctionType
|
|||
(ArgType_General << (ArgType_Shift * 2)) |
|
||||
(ArgType_Double << (ArgType_Shift * 3)) |
|
||||
(ArgType_General << (ArgType_Shift * 4))
|
||||
|
||||
};
|
||||
|
||||
enum class BarrierKind : uint32_t {
|
||||
|
|
|
@ -5424,6 +5424,11 @@ InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choic
|
|||
if (choiceSet[i])
|
||||
continue;
|
||||
|
||||
// If the target wasn't a function we would have veto'ed it
|
||||
// and it will not be in the entries list.
|
||||
if (!targets[i]->is<JSFunction>())
|
||||
continue;
|
||||
|
||||
JSFunction* target = &targets[i]->as<JSFunction>();
|
||||
|
||||
// Eliminate all entries containing the vetoed function from the map.
|
||||
|
|
|
@ -2291,6 +2291,19 @@ ToMIRType(MIRType t)
|
|||
return t;
|
||||
}
|
||||
|
||||
static inline MIRType
|
||||
ToMIRType(ABIArgType argType)
|
||||
{
|
||||
switch (argType & ArgType_Mask) {
|
||||
case ArgType_General: return MIRType::Int32;
|
||||
case ArgType_Double: return MIRType::Double;
|
||||
case ArgType_Float32: return MIRType::Float32;
|
||||
case ArgType_Int64: return MIRType::Int64;
|
||||
default: break;
|
||||
}
|
||||
MOZ_CRASH("unexpected argType");
|
||||
}
|
||||
|
||||
template <class VecT>
|
||||
class ABIArgIter
|
||||
{
|
||||
|
|
|
@ -2446,6 +2446,7 @@ typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t
|
|||
typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
|
||||
int32_t arg3);
|
||||
typedef float (*Prototype_Float32_Float32)(float arg0);
|
||||
typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
|
||||
typedef float (*Prototype_Float32_IntInt)(int arg0, int arg1);
|
||||
|
||||
typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
|
||||
|
@ -2634,6 +2635,21 @@ Simulator::softwareInterrupt(SimInstruction* instr)
|
|||
setCallResultFloat(fresult);
|
||||
break;
|
||||
}
|
||||
case Args_Float32_Float32Float32: {
|
||||
float fval0, fval1;
|
||||
if (UseHardFpABI()) {
|
||||
get_float_from_s_register(0, &fval0);
|
||||
get_float_from_s_register(1, &fval1);
|
||||
} else {
|
||||
fval0 = mozilla::BitwiseCast<float>(arg0);
|
||||
fval1 = mozilla::BitwiseCast<float>(arg1);
|
||||
}
|
||||
Prototype_Float32_Float32Float32 target = reinterpret_cast<Prototype_Float32_Float32Float32>(external);
|
||||
float fresult = target(fval0, fval1);
|
||||
scratchVolatileRegisters(/* scratchFloat = true */);
|
||||
setCallResultFloat(fresult);
|
||||
break;
|
||||
}
|
||||
case Args_Float32_IntInt: {
|
||||
Prototype_Float32_IntInt target = reinterpret_cast<Prototype_Float32_IntInt>(external);
|
||||
float fresult = target(arg0, arg1);
|
||||
|
|
|
@ -161,6 +161,14 @@ struct ImmPtr
|
|||
{
|
||||
void* value;
|
||||
|
||||
struct NoCheckToken {};
|
||||
|
||||
explicit ImmPtr(void* value, NoCheckToken) : value(value)
|
||||
{
|
||||
// A special unchecked variant for contexts where we know it is safe to
|
||||
// use an immptr. This is assuming the caller knows what they're doing.
|
||||
}
|
||||
|
||||
explicit ImmPtr(const void* value) : value(const_cast<void*>(value))
|
||||
{
|
||||
// To make code serialization-safe, wasm compilation should only
|
||||
|
@ -202,7 +210,6 @@ struct ImmPtr
|
|||
{
|
||||
MOZ_ASSERT(!IsCompilingWasm());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// The same as ImmPtr except that the intention is to patch this
|
||||
|
|
|
@ -594,7 +594,7 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of i
|
|||
MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
|
||||
|
||||
// Async Iteration
|
||||
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'")
|
||||
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
|
||||
MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
|
||||
MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
|
||||
MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
|
||||
|
|
|
@ -376,6 +376,7 @@ UNIFIED_SOURCES += [
|
|||
'wasm/WasmIonCompile.cpp',
|
||||
'wasm/WasmJS.cpp',
|
||||
'wasm/WasmModule.cpp',
|
||||
'wasm/WasmRuntime.cpp',
|
||||
'wasm/WasmSignalHandlers.cpp',
|
||||
'wasm/WasmStubs.cpp',
|
||||
'wasm/WasmTable.cpp',
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// |reftest| skip-if(release_or_beta)
|
||||
|
||||
var AsyncGenerator = async function*(){}.constructor;
|
||||
|
||||
function assertSyntaxError(code) {
|
||||
var functionCode = `async function* f() { ${code} }`;
|
||||
assertThrowsInstanceOf(() => AsyncGenerator(code), SyntaxError, "AsyncGenerator:" + code);
|
||||
assertThrowsInstanceOf(() => eval(functionCode), SyntaxError, "eval:" + functionCode);
|
||||
var ieval = eval;
|
||||
assertThrowsInstanceOf(() => ieval(functionCode), SyntaxError, "indirect eval:" + functionCode);
|
||||
}
|
||||
|
||||
assertSyntaxError(`for await (;;) ;`);
|
||||
|
||||
for (var decl of ["", "var", "let", "const"]) {
|
||||
for (var head of ["a", "a = 0", "a, b", "[a]", "[a] = 0", "{a}", "{a} = 0"]) {
|
||||
// Ends with C-style for loop syntax.
|
||||
assertSyntaxError(`for await (${decl} ${head} ;;) ;`);
|
||||
|
||||
// Ends with for-in loop syntax.
|
||||
assertSyntaxError(`for await (${decl} ${head} in null) ;`);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
|
@ -870,12 +870,11 @@ skip script test262/language/statements/async-generator/yield-star-async-next.js
|
|||
skip script test262/language/statements/async-generator/yield-star-async-return.js
|
||||
skip script test262/language/statements/async-generator/yield-star-async-throw.js
|
||||
skip script test262/language/module-code/namespace/internals/delete-non-exported.js
|
||||
|
||||
# https://github.com/tc39/test262/pull/947
|
||||
skip script test262/intl402/NumberFormat/11.1.1_32.js
|
||||
|
||||
# https://github.com/tc39/test262/pull/947
|
||||
skip script test262/intl402/DateTimeFormat/prototype/formatToParts/length.js
|
||||
|
||||
# https://github.com/tc39/test262/pull/947
|
||||
skip script test262/intl402/PluralRules/this-not-ignored.js
|
||||
|
||||
# https://github.com/tc39/test262/pull/961
|
||||
skip script test262/language/statements/async-generator/yield-star-sync-return.js
|
||||
skip script test262/language/statements/async-generator/yield-star-sync-throw.js
|
||||
skip script test262/language/statements/async-generator/yield-star-sync-next.js
|
||||
|
|
|
@ -267,6 +267,9 @@ JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes)
|
|||
if (!caches().init())
|
||||
return false;
|
||||
|
||||
if (!wasm().init())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -279,6 +282,8 @@ JSRuntime::destroyRuntime()
|
|||
|
||||
sharedIntlData.ref().destroyInstance();
|
||||
|
||||
wasm().destroy();
|
||||
|
||||
if (gcInitialized) {
|
||||
/*
|
||||
* Finish any in-progress GCs first. This ensures the parseWaitingOnGC
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "vm/Stack.h"
|
||||
#include "vm/Stopwatch.h"
|
||||
#include "vm/Symbol.h"
|
||||
#include "wasm/WasmRuntime.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
@ -534,6 +535,13 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
/* AsmJSCache callbacks are runtime-wide. */
|
||||
js::UnprotectedData<JS::AsmJSCacheOps> asmJSCacheOps;
|
||||
|
||||
private:
|
||||
// All runtime data needed for wasm and defined in wasm/WasmRuntime.h.
|
||||
js::ActiveThreadData<js::wasm::Runtime> wasmRuntime_;
|
||||
|
||||
public:
|
||||
js::wasm::Runtime& wasm() { return wasmRuntime_.ref(); }
|
||||
|
||||
private:
|
||||
js::UnprotectedData<const JSPrincipals*> trustedPrincipals_;
|
||||
public:
|
||||
|
|
|
@ -1837,6 +1837,7 @@ JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state)
|
|||
if (activation_->isWasm()) {
|
||||
new (storage()) wasm::ProfilingFrameIterator(*activation_->asWasm(), state);
|
||||
// Set savedPrevJitTop_ to the actual jitTop_ from the runtime.
|
||||
AutoNoteSingleThreadedRegion anstr;
|
||||
savedPrevJitTop_ = activation_->cx()->jitTop;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -111,7 +111,8 @@ StaticallyLink(CodeSegment& cs, const LinkData& linkData, JSContext* cx)
|
|||
const Uint32Vector& offsets = linkData.symbolicLinks[imm];
|
||||
for (size_t i = 0; i < offsets.length(); i++) {
|
||||
uint8_t* patchAt = cs.base() + offsets[i];
|
||||
void* target = AddressOf(imm);
|
||||
ABIFunctionType unused;
|
||||
void* target = wasm::AddressOf(imm, &unused);
|
||||
Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
|
||||
PatchedImmPtr(target),
|
||||
PatchedImmPtr((void*)-1));
|
||||
|
@ -295,62 +296,6 @@ FuncImport::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
|||
return sig_.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(Kind kind, Offsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(0),
|
||||
end_(offsets.end),
|
||||
funcIndex_(0),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(begin_ <= end_);
|
||||
#ifdef DEBUG
|
||||
switch (kind_) {
|
||||
case Entry:
|
||||
case DebugTrap:
|
||||
case FarJumpIsland:
|
||||
case Inline:
|
||||
case Throw:
|
||||
case Interrupt:
|
||||
break;
|
||||
case Function:
|
||||
case TrapExit:
|
||||
case ImportJitExit:
|
||||
case ImportInterpExit:
|
||||
MOZ_CRASH("should use more specific constructor");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(Kind kind, CallableOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
funcIndex_(0),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
MOZ_ASSERT(kind_ == ImportJitExit || kind_ == ImportInterpExit || kind_ == TrapExit);
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
funcIndex_(funcIndex),
|
||||
funcLineOrBytecode_(funcLineOrBytecode),
|
||||
funcBeginToNormalEntry_(offsets.normalEntry - begin_),
|
||||
kind_(Function)
|
||||
{
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
MOZ_ASSERT(offsets.normalEntry - begin_ <= UINT8_MAX);
|
||||
}
|
||||
|
||||
static size_t
|
||||
StringLengthWithNullChar(const char* chars)
|
||||
{
|
||||
|
|
|
@ -218,119 +218,6 @@ class FuncImport
|
|||
|
||||
typedef Vector<FuncImport, 0, SystemAllocPolicy> FuncImportVector;
|
||||
|
||||
// A CodeRange describes a single contiguous range of code within a wasm
|
||||
// module's code segment. A CodeRange describes what the code does and, for
|
||||
// function bodies, the name and source coordinates of the function.
|
||||
|
||||
class CodeRange
|
||||
{
|
||||
public:
|
||||
enum Kind {
|
||||
Function, // function definition
|
||||
Entry, // calls into wasm from C++
|
||||
ImportJitExit, // fast-path calling from wasm into JIT code
|
||||
ImportInterpExit, // slow-path calling from wasm into C++ interp
|
||||
TrapExit, // calls C++ to report and jumps to throw stub
|
||||
DebugTrap, // calls C++ to handle debug event
|
||||
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
||||
Inline, // stub that is jumped-to within prologue/epilogue
|
||||
Throw, // special stack-unwinding stub
|
||||
Interrupt // stub executes asynchronously to interrupt wasm
|
||||
};
|
||||
|
||||
private:
|
||||
// All fields are treated as cacheable POD:
|
||||
uint32_t begin_;
|
||||
uint32_t ret_;
|
||||
uint32_t end_;
|
||||
uint32_t funcIndex_;
|
||||
uint32_t funcLineOrBytecode_;
|
||||
uint8_t funcBeginToNormalEntry_;
|
||||
Kind kind_ : 8;
|
||||
|
||||
public:
|
||||
CodeRange() = default;
|
||||
CodeRange(Kind kind, Offsets offsets);
|
||||
CodeRange(Kind kind, CallableOffsets offsets);
|
||||
CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
|
||||
|
||||
// All CodeRanges have a begin and end.
|
||||
|
||||
uint32_t begin() const {
|
||||
return begin_;
|
||||
}
|
||||
uint32_t end() const {
|
||||
return end_;
|
||||
}
|
||||
|
||||
// Other fields are only available for certain CodeRange::Kinds.
|
||||
|
||||
Kind kind() const {
|
||||
return kind_;
|
||||
}
|
||||
|
||||
bool isFunction() const {
|
||||
return kind() == Function;
|
||||
}
|
||||
bool isImportExit() const {
|
||||
return kind() == ImportJitExit || kind() == ImportInterpExit;
|
||||
}
|
||||
bool isTrapExit() const {
|
||||
return kind() == TrapExit;
|
||||
}
|
||||
bool isInline() const {
|
||||
return kind() == Inline;
|
||||
}
|
||||
bool isThunk() const {
|
||||
return kind() == FarJumpIsland;
|
||||
}
|
||||
|
||||
// Every CodeRange except entry and inline stubs are callable and have a
|
||||
// return statement. Asynchronous frame iteration needs to know the offset
|
||||
// of the return instruction to calculate the frame pointer.
|
||||
|
||||
uint32_t ret() const {
|
||||
MOZ_ASSERT(isFunction() || isImportExit() || isTrapExit());
|
||||
return ret_;
|
||||
}
|
||||
|
||||
// Function CodeRanges have two entry points: one for normal calls (with a
|
||||
// known signature) and one for table calls (which involves dynamic
|
||||
// signature checking).
|
||||
|
||||
uint32_t funcTableEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin_;
|
||||
}
|
||||
uint32_t funcNormalEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin_ + funcBeginToNormalEntry_;
|
||||
}
|
||||
uint32_t funcIndex() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return funcIndex_;
|
||||
}
|
||||
uint32_t funcLineOrBytecode() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return funcLineOrBytecode_;
|
||||
}
|
||||
|
||||
// A sorted array of CodeRanges can be looked up via BinarySearch and PC.
|
||||
|
||||
struct PC {
|
||||
size_t offset;
|
||||
explicit PC(size_t offset) : offset(offset) {}
|
||||
bool operator==(const CodeRange& rhs) const {
|
||||
return offset >= rhs.begin() && offset < rhs.end();
|
||||
}
|
||||
bool operator<(const CodeRange& rhs) const {
|
||||
return offset < rhs.begin();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
|
||||
|
||||
// A wasm module can either use no memory, a unshared memory (ArrayBuffer) or
|
||||
// shared memory (SharedArrayBuffer).
|
||||
|
||||
|
|
|
@ -553,6 +553,7 @@ ProfilingFrameIterator::initFromExitFP()
|
|||
break;
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::ImportNativeExit:
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::Inline:
|
||||
|
@ -591,9 +592,23 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
// If pc isn't in the instance's code, we must have exited the code via an
|
||||
// exit trampoline or signal handler.
|
||||
code_ = activation_->compartment()->wasm.lookupCode(state.pc);
|
||||
|
||||
const CodeRange* codeRange = nullptr;
|
||||
uint8_t* codeBase = nullptr;
|
||||
if (!code_) {
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
// Optimized builtin exits (see MaybeGetMatchingBuiltin in
|
||||
// WasmInstance.cpp) are outside module's code.
|
||||
AutoNoteSingleThreadedRegion anstr;
|
||||
if (BuiltinThunk* thunk = activation_->cx()->runtime()->wasm().lookupBuiltin(state.pc)) {
|
||||
codeRange = &thunk->codeRange;
|
||||
codeBase = (uint8_t*) thunk->base;
|
||||
} else {
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
codeRange = code_->lookupRange(state.pc);
|
||||
codeBase = code_->segment().base();
|
||||
}
|
||||
|
||||
// When the pc is inside the prologue/epilogue, the innermost call's Frame
|
||||
|
@ -606,10 +621,9 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
uint8_t* pc = (uint8_t*)state.pc;
|
||||
void** sp = (void**)state.sp;
|
||||
|
||||
const CodeRange* codeRange = code_->lookupRange(pc);
|
||||
uint32_t offsetInModule = pc - code_->segment().base();
|
||||
MOZ_ASSERT(offsetInModule >= codeRange->begin());
|
||||
MOZ_ASSERT(offsetInModule < codeRange->end());
|
||||
uint32_t offsetInCode = pc - codeBase;
|
||||
MOZ_ASSERT(offsetInCode >= codeRange->begin());
|
||||
MOZ_ASSERT(offsetInCode < codeRange->end());
|
||||
|
||||
// Compute the offset of the pc from the (normal) entry of the code range.
|
||||
// The stack state of the pc for the entire table-entry is equivalent to
|
||||
|
@ -618,12 +632,12 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
// pc-at-normal-entry case.
|
||||
uint32_t offsetFromEntry;
|
||||
if (codeRange->isFunction()) {
|
||||
if (offsetInModule < codeRange->funcNormalEntry())
|
||||
if (offsetInCode < codeRange->funcNormalEntry())
|
||||
offsetFromEntry = 0;
|
||||
else
|
||||
offsetFromEntry = offsetInModule - codeRange->funcNormalEntry();
|
||||
offsetFromEntry = offsetInCode - codeRange->funcNormalEntry();
|
||||
} else {
|
||||
offsetFromEntry = offsetInModule - codeRange->begin();
|
||||
offsetFromEntry = offsetInCode - codeRange->begin();
|
||||
}
|
||||
|
||||
switch (codeRange->kind()) {
|
||||
|
@ -631,6 +645,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
case CodeRange::FarJumpIsland:
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::ImportNativeExit:
|
||||
case CodeRange::TrapExit:
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
if (offsetFromEntry == BeforePushRetAddr || codeRange->isThunk()) {
|
||||
|
@ -658,11 +673,11 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
callerPC_ = ReturnAddressFromFP(sp);
|
||||
callerFP_ = fp;
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
|
||||
} else if (offsetInModule == codeRange->ret() - PoppedFP) {
|
||||
} else if (offsetInCode == codeRange->ret() - PoppedFP) {
|
||||
// The callerFP field of the Frame has been popped into fp.
|
||||
callerPC_ = sp[1];
|
||||
callerFP_ = fp;
|
||||
} else if (offsetInModule == codeRange->ret()) {
|
||||
} else if (offsetInCode == codeRange->ret()) {
|
||||
// Both the TLS and callerFP fields have been popped and fp now
|
||||
// points to the caller's frame.
|
||||
callerPC_ = sp[0];
|
||||
|
@ -740,6 +755,7 @@ ProfilingFrameIterator::operator++()
|
|||
case CodeRange::Function:
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::ImportNativeExit:
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::Inline:
|
||||
|
@ -769,6 +785,7 @@ ProfilingFrameIterator::label() const
|
|||
// devtools/client/performance/modules/logic/frame-utils.js
|
||||
static const char* importJitDescription = "fast FFI trampoline (in wasm)";
|
||||
static const char* importInterpDescription = "slow FFI trampoline (in wasm)";
|
||||
static const char* importNativeDescription = "fast FFI trampoline to native (in wasm)";
|
||||
static const char* trapDescription = "trap handling (in wasm)";
|
||||
static const char* debugTrapDescription = "debug trap handling (in wasm)";
|
||||
|
||||
|
@ -779,6 +796,8 @@ ProfilingFrameIterator::label() const
|
|||
return importJitDescription;
|
||||
case ExitReason::ImportInterp:
|
||||
return importInterpDescription;
|
||||
case ExitReason::ImportNative:
|
||||
return importNativeDescription;
|
||||
case ExitReason::Trap:
|
||||
return trapDescription;
|
||||
case ExitReason::DebugTrap:
|
||||
|
@ -789,6 +808,7 @@ ProfilingFrameIterator::label() const
|
|||
case CodeRange::Function: return code_->profilingLabel(codeRange_->funcIndex());
|
||||
case CodeRange::Entry: return "entry trampoline (in wasm)";
|
||||
case CodeRange::ImportJitExit: return importJitDescription;
|
||||
case CodeRange::ImportNativeExit: return importNativeDescription;
|
||||
case CodeRange::ImportInterpExit: return importInterpDescription;
|
||||
case CodeRange::TrapExit: return trapDescription;
|
||||
case CodeRange::DebugTrap: return debugTrapDescription;
|
||||
|
|
|
@ -89,6 +89,7 @@ enum class ExitReason : uint32_t
|
|||
None, // default state, the pc is in wasm code
|
||||
ImportJit, // fast-path call directly into JIT code
|
||||
ImportInterp, // slow-path call into C++ Invoke()
|
||||
ImportNative, // fast-path call directly into native C++ code
|
||||
Trap, // call to trap handler for the trap in WasmActivation::trap
|
||||
DebugTrap // call to debug trap handler
|
||||
};
|
||||
|
|
|
@ -414,6 +414,7 @@ ModuleGenerator::patchCallSites()
|
|||
}
|
||||
}
|
||||
|
||||
masm_.flushBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "jit/JitCommon.h"
|
||||
|
||||
#include "wasm/WasmModule.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
@ -315,6 +317,128 @@ Instance::currentMemory_i32(Instance* instance)
|
|||
return byteLength / wasm::PageSize;
|
||||
}
|
||||
|
||||
// asm.js has the ability to call directly into Math builtins, which wasm can't
|
||||
// do. Instead, wasm code generators have to pass the builtins as function
|
||||
// imports, resulting in slow import calls.
|
||||
//
|
||||
// However, we can optimize this by detecting that an import is just a JSNative
|
||||
// builtin and have wasm call straight to the builtin's C++ code, since the
|
||||
// ABIs perfectly match at the call site.
|
||||
//
|
||||
// Even though we could call into float32 variants of the math functions, we
|
||||
// do not do it, so as not to change the results.
|
||||
|
||||
#define FOREACH_UNCACHED_MATH_BUILTIN(_) \
|
||||
_(math_sin, MathSin) \
|
||||
_(math_tan, MathTan) \
|
||||
_(math_cos, MathCos) \
|
||||
_(math_exp, MathExp) \
|
||||
_(math_log, MathLog) \
|
||||
_(math_asin, MathASin) \
|
||||
_(math_atan, MathATan) \
|
||||
_(math_acos, MathACos) \
|
||||
_(math_log10, MathLog10) \
|
||||
_(math_log2, MathLog2) \
|
||||
_(math_log1p, MathLog1P) \
|
||||
_(math_expm1, MathExpM1) \
|
||||
_(math_sinh, MathSinH) \
|
||||
_(math_tanh, MathTanH) \
|
||||
_(math_cosh, MathCosH) \
|
||||
_(math_asinh, MathASinH) \
|
||||
_(math_atanh, MathATanH) \
|
||||
_(math_acosh, MathACosH) \
|
||||
_(math_sign, MathSign) \
|
||||
_(math_trunc, MathTrunc) \
|
||||
_(math_cbrt, MathCbrt)
|
||||
|
||||
#define UNARY_FLOAT_WRAPPER(func) \
|
||||
float func##_f32(float x) { \
|
||||
return float(func(double(x))); \
|
||||
}
|
||||
|
||||
#define BINARY_FLOAT_WRAPPER(func) \
|
||||
float func##_f32(float x, float y) { \
|
||||
return float(func(double(x), double(y))); \
|
||||
}
|
||||
|
||||
#define DEFINE_FLOAT_WRAPPER(name, _) UNARY_FLOAT_WRAPPER(name##_uncached)
|
||||
FOREACH_UNCACHED_MATH_BUILTIN(DEFINE_FLOAT_WRAPPER)
|
||||
|
||||
BINARY_FLOAT_WRAPPER(ecmaAtan2)
|
||||
BINARY_FLOAT_WRAPPER(ecmaHypot)
|
||||
BINARY_FLOAT_WRAPPER(ecmaPow)
|
||||
|
||||
#undef DEFINE_FLOAT_WRAPPER
|
||||
#undef BINARY_FLOAT_WRAPPER
|
||||
#undef UNARY_FLOAT_WRAPPER
|
||||
|
||||
static void*
|
||||
IsMatchingBuiltin(HandleFunction f, const Sig& sig)
|
||||
{
|
||||
if (!f->isNative() || !f->jitInfo() || f->jitInfo()->type() != JSJitInfo::InlinableNative)
|
||||
return nullptr;
|
||||
|
||||
ExprType ret = sig.ret();
|
||||
const ValTypeVector& args = sig.args();
|
||||
|
||||
#define UNARY_BUILTIN(double_func, float_func) \
|
||||
if (args.length() != 1) \
|
||||
break; \
|
||||
if (args[0] == ValType::F64 && ret == ExprType::F64) \
|
||||
return JS_FUNC_TO_DATA_PTR(void*, double_func); \
|
||||
if (args[0] == ValType::F32 && ret == ExprType::F32) \
|
||||
return JS_FUNC_TO_DATA_PTR(void*, float_func); \
|
||||
break;
|
||||
|
||||
#define BINARY_BUILTIN(double_func, float_func) \
|
||||
if (args.length() != 2) \
|
||||
break; \
|
||||
if (args[0] == ValType::F64 && args[1] == ValType::F64 && ret == ExprType::F64) \
|
||||
return JS_FUNC_TO_DATA_PTR(void*, double_func); \
|
||||
if (args[0] == ValType::F32 && args[1] == ValType::F32 && ret == ExprType::F32) \
|
||||
return JS_FUNC_TO_DATA_PTR(void*, float_func); \
|
||||
break;
|
||||
|
||||
switch (f->jitInfo()->inlinableNative) {
|
||||
#define MAKE_CASE(funcName, inlinableNative) \
|
||||
case InlinableNative::inlinableNative: \
|
||||
UNARY_BUILTIN(funcName##_uncached, funcName##_uncached_f32)
|
||||
|
||||
FOREACH_UNCACHED_MATH_BUILTIN(MAKE_CASE)
|
||||
|
||||
case InlinableNative::MathATan2:
|
||||
BINARY_BUILTIN(ecmaAtan2, ecmaAtan2_f32)
|
||||
case InlinableNative::MathHypot:
|
||||
BINARY_BUILTIN(ecmaHypot, ecmaHypot_f32)
|
||||
case InlinableNative::MathPow:
|
||||
BINARY_BUILTIN(ecmaPow, ecmaPow_f32)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#undef MAKE_CASE
|
||||
#undef UNARY_BUILTIN
|
||||
#undef BINARY_BUILTIN
|
||||
#undef FOREACH_UNCACHED_MATH_BUILTIN
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void*
|
||||
MaybeGetMatchingBuiltin(JSContext* cx, HandleFunction f, const Sig& sig)
|
||||
{
|
||||
void* funcPtr = IsMatchingBuiltin(f, sig);
|
||||
if (!funcPtr)
|
||||
return nullptr;
|
||||
|
||||
void* thunkPtr = nullptr;
|
||||
if (!cx->runtime()->wasm().getBuiltinThunk(cx, funcPtr, sig, &thunkPtr))
|
||||
return nullptr;
|
||||
|
||||
return thunkPtr;
|
||||
}
|
||||
|
||||
Instance::Instance(JSContext* cx,
|
||||
Handle<WasmInstanceObject*> object,
|
||||
UniqueCode code,
|
||||
|
@ -355,6 +479,11 @@ Instance::Instance(JSContext* cx,
|
|||
import.code = calleeInstance.codeSegment().base() + codeRange.funcNormalEntry();
|
||||
import.baselineScript = nullptr;
|
||||
import.obj = calleeInstanceObj;
|
||||
} else if (void* thunk = MaybeGetMatchingBuiltin(cx, f, fi.sig())) {
|
||||
import.tls = tlsData();
|
||||
import.code = thunk;
|
||||
import.baselineScript = nullptr;
|
||||
import.obj = f;
|
||||
} else {
|
||||
import.tls = tlsData();
|
||||
import.code = codeBase() + fi.interpExitCodeOffset();
|
||||
|
|
|
@ -77,21 +77,13 @@ class Instance
|
|||
FuncImportTls& funcImportTls(const FuncImport& fi);
|
||||
TableTls& tableTls(const TableDesc& td) const;
|
||||
|
||||
// Import call slow paths which are called directly from wasm code.
|
||||
friend void* AddressOf(SymbolicAddress);
|
||||
static int32_t callImport_void(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
|
||||
static uint32_t currentMemory_i32(Instance* instance);
|
||||
bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
|
||||
MutableHandleValue rval);
|
||||
|
||||
// Only WasmInstanceObject can call the private trace function.
|
||||
friend class js::WasmInstanceObject;
|
||||
void tracePrivate(JSTracer* trc);
|
||||
|
||||
bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
|
||||
MutableHandleValue rval);
|
||||
|
||||
public:
|
||||
Instance(JSContext* cx,
|
||||
HandleWasmInstanceObject object,
|
||||
|
@ -165,6 +157,15 @@ class Instance
|
|||
Table::SeenSet* seenTables,
|
||||
size_t* code,
|
||||
size_t* data) const;
|
||||
|
||||
public:
|
||||
// Functions to be called directly from wasm code.
|
||||
static int32_t callImport_void(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
|
||||
static uint32_t currentMemory_i32(Instance* instance);
|
||||
};
|
||||
|
||||
typedef UniquePtr<Instance> UniqueInstance;
|
||||
|
|
|
@ -0,0 +1,691 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2017 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "wasm/WasmRuntime.h"
|
||||
|
||||
#include "mozilla/BinarySearch.h"
|
||||
|
||||
#include "fdlibm.h"
|
||||
|
||||
#include "jslibmath.h"
|
||||
|
||||
#include "jit/MacroAssembler.h"
|
||||
|
||||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmStubs.h"
|
||||
|
||||
#include "vm/Debugger-inl.h"
|
||||
#include "vm/Stack-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace jit;
|
||||
using namespace wasm;
|
||||
|
||||
using mozilla::BinarySearchIf;
|
||||
using mozilla::IsNaN;
|
||||
|
||||
static const unsigned BUILTIN_THUNK_LIFO_SIZE = 128;
|
||||
static const CodeKind BUILTIN_THUNK_CODEKIND = CodeKind::OTHER_CODE;
|
||||
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
extern "C" {
|
||||
|
||||
extern MOZ_EXPORT int64_t
|
||||
__aeabi_idivmod(int, int);
|
||||
|
||||
extern MOZ_EXPORT int64_t
|
||||
__aeabi_uidivmod(int, int);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static void*
|
||||
WasmHandleExecutionInterrupt()
|
||||
{
|
||||
WasmActivation* activation = JSContext::innermostWasmActivation();
|
||||
|
||||
// wasm::Compartment requires notification when execution is interrupted in
|
||||
// the compartment. Only the innermost compartment has been interrupted;
|
||||
// enclosing compartments necessarily exited through an exit stub.
|
||||
activation->compartment()->wasm.setInterrupted(true);
|
||||
bool success = CheckForInterrupt(activation->cx());
|
||||
activation->compartment()->wasm.setInterrupted(false);
|
||||
|
||||
// Preserve the invariant that having a non-null resumePC means that we are
|
||||
// handling an interrupt.
|
||||
void* resumePC = activation->resumePC();
|
||||
activation->setResumePC(nullptr);
|
||||
|
||||
// Return the resumePC if execution can continue or null if execution should
|
||||
// jump to the throw stub.
|
||||
return success ? resumePC : nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmHandleDebugTrap()
|
||||
{
|
||||
WasmActivation* activation = JSContext::innermostWasmActivation();
|
||||
MOZ_ASSERT(activation);
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
FrameIterator iter(activation);
|
||||
MOZ_ASSERT(iter.debugEnabled());
|
||||
const CallSite* site = iter.debugTrapCallsite();
|
||||
MOZ_ASSERT(site);
|
||||
if (site->kind() == CallSite::EnterFrame) {
|
||||
if (!iter.instance()->enterFrameTrapsEnabled())
|
||||
return true;
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->setIsDebuggee();
|
||||
frame->observe(cx);
|
||||
// TODO call onEnterFrame
|
||||
JSTrapStatus status = Debugger::onEnterFrame(cx, frame);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// Ignoring forced return (JSTRAP_RETURN) -- changing code execution
|
||||
// order is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle JSTRAP_RETURN and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onEnterFrame");
|
||||
return false;
|
||||
}
|
||||
return status == JSTRAP_CONTINUE;
|
||||
}
|
||||
if (site->kind() == CallSite::LeaveFrame) {
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->updateReturnJSValue();
|
||||
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, true);
|
||||
frame->leave(cx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
Code& code = iter.instance()->code();
|
||||
MOZ_ASSERT(code.hasBreakpointTrapAtOffset(site->lineOrBytecode()));
|
||||
if (code.stepModeEnabled(frame->funcIndex())) {
|
||||
RootedValue result(cx, UndefinedValue());
|
||||
JSTrapStatus status = Debugger::onSingleStep(cx, &result);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// TODO properly handle JSTRAP_RETURN.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onSingleStep");
|
||||
return false;
|
||||
}
|
||||
if (status != JSTRAP_CONTINUE)
|
||||
return false;
|
||||
}
|
||||
if (code.hasBreakpointSite(site->lineOrBytecode())) {
|
||||
RootedValue result(cx, UndefinedValue());
|
||||
JSTrapStatus status = Debugger::onTrap(cx, &result);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// TODO properly handle JSTRAP_RETURN.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from breakpoint handler");
|
||||
return false;
|
||||
}
|
||||
if (status != JSTRAP_CONTINUE)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static WasmActivation*
|
||||
WasmHandleThrow()
|
||||
{
|
||||
JSContext* cx = TlsContext.get();
|
||||
|
||||
WasmActivation* activation = cx->wasmActivationStack();
|
||||
MOZ_ASSERT(activation);
|
||||
|
||||
// FrameIterator iterates down wasm frames in the activation starting at
|
||||
// WasmActivation::exitFP. Pass Unwind::True to pop WasmActivation::exitFP
|
||||
// once each time FrameIterator is incremented, ultimately leaving exitFP
|
||||
// null when the FrameIterator is done(). This is necessary to prevent a
|
||||
// DebugFrame from being observed again after we just called onLeaveFrame
|
||||
// (which would lead to the frame being re-added to the map of live frames,
|
||||
// right as it becomes trash).
|
||||
FrameIterator iter(activation, FrameIterator::Unwind::True);
|
||||
if (iter.done())
|
||||
return activation;
|
||||
|
||||
// Live wasm code on the stack is kept alive (in wasm::TraceActivations) by
|
||||
// marking the instance of every wasm::Frame found by FrameIterator.
|
||||
// However, as explained above, we're popping frames while iterating which
|
||||
// means that a GC during this loop could collect the code of frames whose
|
||||
// code is still on the stack. This is actually mostly fine: as soon as we
|
||||
// return to the throw stub, the entire stack will be popped as a whole,
|
||||
// returning to the C++ caller. However, we must keep the throw stub alive
|
||||
// itself which is owned by the innermost instance.
|
||||
RootedWasmInstanceObject keepAlive(cx, iter.instance()->object());
|
||||
|
||||
for (; !iter.done(); ++iter) {
|
||||
if (!iter.debugEnabled())
|
||||
continue;
|
||||
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->clearReturnJSValue();
|
||||
|
||||
// Assume JSTRAP_ERROR status if no exception is pending --
|
||||
// no onExceptionUnwind handlers must be fired.
|
||||
if (cx->isExceptionPending()) {
|
||||
JSTrapStatus status = Debugger::onExceptionUnwind(cx, frame);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// Unexpected trap return -- raising error since throw recovery
|
||||
// is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle JSTRAP_RETURN and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onExceptionUnwind");
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, false);
|
||||
if (ok) {
|
||||
// Unexpected success from the handler onLeaveFrame -- raising error
|
||||
// since throw recovery is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle success and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
|
||||
}
|
||||
frame->leave(cx);
|
||||
}
|
||||
|
||||
return activation;
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportTrap(int32_t trapIndex)
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
|
||||
MOZ_ASSERT(trapIndex < int32_t(Trap::Limit) && trapIndex >= 0);
|
||||
Trap trap = Trap(trapIndex);
|
||||
|
||||
unsigned errorNumber;
|
||||
switch (trap) {
|
||||
case Trap::Unreachable:
|
||||
errorNumber = JSMSG_WASM_UNREACHABLE;
|
||||
break;
|
||||
case Trap::IntegerOverflow:
|
||||
errorNumber = JSMSG_WASM_INTEGER_OVERFLOW;
|
||||
break;
|
||||
case Trap::InvalidConversionToInteger:
|
||||
errorNumber = JSMSG_WASM_INVALID_CONVERSION;
|
||||
break;
|
||||
case Trap::IntegerDivideByZero:
|
||||
errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
|
||||
break;
|
||||
case Trap::IndirectCallToNull:
|
||||
errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
|
||||
break;
|
||||
case Trap::IndirectCallBadSig:
|
||||
errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
|
||||
break;
|
||||
case Trap::ImpreciseSimdConversion:
|
||||
errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
|
||||
break;
|
||||
case Trap::OutOfBounds:
|
||||
errorNumber = JSMSG_WASM_OUT_OF_BOUNDS;
|
||||
break;
|
||||
case Trap::StackOverflow:
|
||||
errorNumber = JSMSG_OVER_RECURSED;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected trap");
|
||||
}
|
||||
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportOutOfBounds()
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportUnalignedAccess()
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNALIGNED_ACCESS);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
CoerceInPlace_ToInt32(MutableHandleValue val)
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
|
||||
int32_t i32;
|
||||
if (!ToInt32(cx, val, &i32))
|
||||
return false;
|
||||
val.set(Int32Value(i32));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
CoerceInPlace_ToNumber(MutableHandleValue val)
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
|
||||
double dbl;
|
||||
if (!ToNumber(cx, val, &dbl))
|
||||
return false;
|
||||
val.set(DoubleValue(dbl));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
int64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
int64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(x != INT64_MIN || y != -1);
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x / y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
UDivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x / y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
ModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
int64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
int64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(x != INT64_MIN || y != -1);
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x % y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x % y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
TruncateDoubleToInt64(double input)
|
||||
{
|
||||
// Note: INT64_MAX is not representable in double. It is actually
|
||||
// INT64_MAX + 1. Therefore also sending the failure value.
|
||||
if (input >= double(INT64_MAX) || input < double(INT64_MIN) || IsNaN(input))
|
||||
return 0x8000000000000000;
|
||||
return int64_t(input);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
TruncateDoubleToUint64(double input)
|
||||
{
|
||||
// Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
|
||||
// Therefore also sending the failure value.
|
||||
if (input >= double(UINT64_MAX) || input <= -1.0 || IsNaN(input))
|
||||
return 0x8000000000000000;
|
||||
return uint64_t(input);
|
||||
}
|
||||
|
||||
static double
|
||||
Int64ToDouble(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
|
||||
return double(x);
|
||||
}
|
||||
|
||||
static float
|
||||
Int64ToFloat32(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
|
||||
return float(x);
|
||||
}
|
||||
|
||||
static double
|
||||
Uint64ToDouble(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
|
||||
return double(x);
|
||||
}
|
||||
|
||||
static float
|
||||
Uint64ToFloat32(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
|
||||
return float(x);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static inline void*
|
||||
FuncCast(F* funcPtr, ABIFunctionType abiType)
|
||||
{
|
||||
void* pf = JS_FUNC_TO_DATA_PTR(void*, funcPtr);
|
||||
#ifdef JS_SIMULATOR
|
||||
pf = Simulator::RedirectNativeFunction(pf, abiType);
|
||||
#endif
|
||||
return pf;
|
||||
}
|
||||
|
||||
void*
|
||||
wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType)
|
||||
{
|
||||
switch (imm) {
|
||||
case SymbolicAddress::HandleExecutionInterrupt:
|
||||
*abiType = Args_General0;
|
||||
return FuncCast(WasmHandleExecutionInterrupt, *abiType);
|
||||
case SymbolicAddress::HandleDebugTrap:
|
||||
*abiType = Args_General0;
|
||||
return FuncCast(WasmHandleDebugTrap, *abiType);
|
||||
case SymbolicAddress::HandleThrow:
|
||||
*abiType = Args_General0;
|
||||
return FuncCast(WasmHandleThrow, *abiType);
|
||||
case SymbolicAddress::ReportTrap:
|
||||
*abiType = Args_General1;
|
||||
return FuncCast(WasmReportTrap, *abiType);
|
||||
case SymbolicAddress::ReportOutOfBounds:
|
||||
*abiType = Args_General0;
|
||||
return FuncCast(WasmReportOutOfBounds, *abiType);
|
||||
case SymbolicAddress::ReportUnalignedAccess:
|
||||
*abiType = Args_General0;
|
||||
return FuncCast(WasmReportUnalignedAccess, *abiType);
|
||||
case SymbolicAddress::CallImport_Void:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(Instance::callImport_void, *abiType);
|
||||
case SymbolicAddress::CallImport_I32:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(Instance::callImport_i32, *abiType);
|
||||
case SymbolicAddress::CallImport_I64:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(Instance::callImport_i64, *abiType);
|
||||
case SymbolicAddress::CallImport_F64:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(Instance::callImport_f64, *abiType);
|
||||
case SymbolicAddress::CoerceInPlace_ToInt32:
|
||||
*abiType = Args_General1;
|
||||
return FuncCast(CoerceInPlace_ToInt32, *abiType);
|
||||
case SymbolicAddress::CoerceInPlace_ToNumber:
|
||||
*abiType = Args_General1;
|
||||
return FuncCast(CoerceInPlace_ToNumber, *abiType);
|
||||
case SymbolicAddress::ToInt32:
|
||||
*abiType = Args_Int_Double;
|
||||
return FuncCast<int32_t (double)>(JS::ToInt32, *abiType);
|
||||
case SymbolicAddress::DivI64:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(DivI64, *abiType);
|
||||
case SymbolicAddress::UDivI64:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(UDivI64, *abiType);
|
||||
case SymbolicAddress::ModI64:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(ModI64, *abiType);
|
||||
case SymbolicAddress::UModI64:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(UModI64, *abiType);
|
||||
case SymbolicAddress::TruncateDoubleToUint64:
|
||||
*abiType = Args_Int64_Double;
|
||||
return FuncCast(TruncateDoubleToUint64, *abiType);
|
||||
case SymbolicAddress::TruncateDoubleToInt64:
|
||||
*abiType = Args_Int64_Double;
|
||||
return FuncCast(TruncateDoubleToInt64, *abiType);
|
||||
case SymbolicAddress::Uint64ToDouble:
|
||||
*abiType = Args_Double_IntInt;
|
||||
return FuncCast(Uint64ToDouble, *abiType);
|
||||
case SymbolicAddress::Uint64ToFloat32:
|
||||
*abiType = Args_Float32_IntInt;
|
||||
return FuncCast(Uint64ToFloat32, *abiType);
|
||||
case SymbolicAddress::Int64ToDouble:
|
||||
*abiType = Args_Double_IntInt;
|
||||
return FuncCast(Int64ToDouble, *abiType);
|
||||
case SymbolicAddress::Int64ToFloat32:
|
||||
*abiType = Args_Float32_IntInt;
|
||||
return FuncCast(Int64ToFloat32, *abiType);
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
case SymbolicAddress::aeabi_idivmod:
|
||||
*abiType = Args_General2;
|
||||
return FuncCast(__aeabi_idivmod, *abiType);
|
||||
case SymbolicAddress::aeabi_uidivmod:
|
||||
*abiType = Args_General2;
|
||||
return FuncCast(__aeabi_uidivmod, *abiType);
|
||||
case SymbolicAddress::AtomicCmpXchg:
|
||||
*abiType = Args_General5;
|
||||
return FuncCast(atomics_cmpxchg_asm_callout, *abiType);
|
||||
case SymbolicAddress::AtomicXchg:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(atomics_xchg_asm_callout, *abiType);
|
||||
case SymbolicAddress::AtomicFetchAdd:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(atomics_add_asm_callout, *abiType);
|
||||
case SymbolicAddress::AtomicFetchSub:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(atomics_sub_asm_callout, *abiType);
|
||||
case SymbolicAddress::AtomicFetchAnd:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(atomics_and_asm_callout, *abiType);
|
||||
case SymbolicAddress::AtomicFetchOr:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(atomics_or_asm_callout, *abiType);
|
||||
case SymbolicAddress::AtomicFetchXor:
|
||||
*abiType = Args_General4;
|
||||
return FuncCast(atomics_xor_asm_callout, *abiType);
|
||||
#endif
|
||||
case SymbolicAddress::ModD:
|
||||
*abiType = Args_Double_DoubleDouble;
|
||||
return FuncCast(NumberMod, *abiType);
|
||||
case SymbolicAddress::SinD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(sin, *abiType);
|
||||
case SymbolicAddress::CosD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(cos, *abiType);
|
||||
case SymbolicAddress::TanD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(tan, *abiType);
|
||||
case SymbolicAddress::ASinD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::asin, *abiType);
|
||||
case SymbolicAddress::ACosD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::acos, *abiType);
|
||||
case SymbolicAddress::ATanD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::atan, *abiType);
|
||||
case SymbolicAddress::CeilD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::ceil, *abiType);
|
||||
case SymbolicAddress::CeilF:
|
||||
*abiType = Args_Float32_Float32;
|
||||
return FuncCast<float (float)>(fdlibm::ceilf, *abiType);
|
||||
case SymbolicAddress::FloorD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::floor, *abiType);
|
||||
case SymbolicAddress::FloorF:
|
||||
*abiType = Args_Float32_Float32;
|
||||
return FuncCast<float (float)>(fdlibm::floorf, *abiType);
|
||||
case SymbolicAddress::TruncD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::trunc, *abiType);
|
||||
case SymbolicAddress::TruncF:
|
||||
*abiType = Args_Float32_Float32;
|
||||
return FuncCast<float (float)>(fdlibm::truncf, *abiType);
|
||||
case SymbolicAddress::NearbyIntD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::nearbyint, *abiType);
|
||||
case SymbolicAddress::NearbyIntF:
|
||||
*abiType = Args_Float32_Float32;
|
||||
return FuncCast<float (float)>(fdlibm::nearbyintf, *abiType);
|
||||
case SymbolicAddress::ExpD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::exp, *abiType);
|
||||
case SymbolicAddress::LogD:
|
||||
*abiType = Args_Double_Double;
|
||||
return FuncCast<double (double)>(fdlibm::log, *abiType);
|
||||
case SymbolicAddress::PowD:
|
||||
*abiType = Args_Double_DoubleDouble;
|
||||
return FuncCast(ecmaPow, *abiType);
|
||||
case SymbolicAddress::ATan2D:
|
||||
*abiType = Args_Double_DoubleDouble;
|
||||
return FuncCast(ecmaAtan2, *abiType);
|
||||
case SymbolicAddress::GrowMemory:
|
||||
*abiType = Args_General2;
|
||||
return FuncCast(Instance::growMemory_i32, *abiType);
|
||||
case SymbolicAddress::CurrentMemory:
|
||||
*abiType = Args_General1;
|
||||
return FuncCast(Instance::currentMemory_i32, *abiType);
|
||||
case SymbolicAddress::Limit:
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Bad SymbolicAddress");
|
||||
}
|
||||
|
||||
static bool
|
||||
GenerateBuiltinThunk(JSContext* cx, void* func, ABIFunctionType abiType, UniqueBuiltinThunk* thunk)
|
||||
{
|
||||
if (!cx->compartment()->ensureJitCompartmentExists(cx))
|
||||
return false;
|
||||
|
||||
LifoAlloc lifo(BUILTIN_THUNK_LIFO_SIZE);
|
||||
TempAllocator tempAlloc(&lifo);
|
||||
MacroAssembler masm(MacroAssembler::WasmToken(), tempAlloc);
|
||||
|
||||
CallableOffsets offsets = GenerateBuiltinImportExit(masm, abiType, func);
|
||||
|
||||
masm.finish();
|
||||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
// The executable allocator operates on pointer-aligned sizes.
|
||||
uint32_t codeLength = AlignBytes(masm.bytesNeeded(), sizeof(void*));
|
||||
|
||||
ExecutablePool* pool = nullptr;
|
||||
ExecutableAllocator& allocator = cx->runtime()->jitRuntime()->execAlloc();
|
||||
uint8_t* codeBase = (uint8_t*) allocator.alloc(cx, codeLength, &pool, BUILTIN_THUNK_CODEKIND);
|
||||
if (!codeBase)
|
||||
return false;
|
||||
|
||||
{
|
||||
AutoWritableJitCode awjc(cx->runtime(), codeBase, codeLength);
|
||||
AutoFlushICache afc("GenerateBuiltinThunk");
|
||||
|
||||
masm.executableCopy(codeBase);
|
||||
memset(codeBase + masm.bytesNeeded(), 0, codeLength - masm.bytesNeeded());
|
||||
|
||||
MOZ_ASSERT(!masm.numSymbolicAccesses(), "doesn't need any patching");
|
||||
}
|
||||
|
||||
*thunk = js::MakeUnique<BuiltinThunk>(codeBase, codeLength, pool, offsets);
|
||||
return !!*thunk;
|
||||
}
|
||||
|
||||
struct BuiltinMatcher
|
||||
{
|
||||
const uint8_t* address;
|
||||
explicit BuiltinMatcher(const uint8_t* address) : address(address) {}
|
||||
int operator()(const UniqueBuiltinThunk& thunk) const {
|
||||
if (address < thunk->base)
|
||||
return -1;
|
||||
if (uintptr_t(address) >= uintptr_t(thunk->base) + thunk->size)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
wasm::Runtime::getBuiltinThunk(JSContext* cx, void* funcPtr, ABIFunctionType abiType,
|
||||
void** thunkPtr)
|
||||
{
|
||||
TypedFuncPtr lookup(funcPtr, abiType);
|
||||
auto ptr = builtinThunkMap_.lookupForAdd(lookup);
|
||||
if (ptr) {
|
||||
*thunkPtr = ptr->value();
|
||||
return true;
|
||||
}
|
||||
|
||||
UniqueBuiltinThunk thunk;
|
||||
if (!GenerateBuiltinThunk(cx, funcPtr, abiType, &thunk))
|
||||
return false;
|
||||
|
||||
// Maintain sorted order of thunk addresses.
|
||||
size_t i;
|
||||
size_t size = builtinThunkVector_.length();
|
||||
if (BinarySearchIf(builtinThunkVector_, 0, size, BuiltinMatcher(thunk->base), &i))
|
||||
MOZ_CRASH("clobbering memory");
|
||||
|
||||
*thunkPtr = thunk->base;
|
||||
|
||||
return builtinThunkVector_.insert(builtinThunkVector_.begin() + i, Move(thunk)) &&
|
||||
builtinThunkMap_.add(ptr, lookup, *thunkPtr);
|
||||
}
|
||||
|
||||
static ABIFunctionType
|
||||
ToABIFunctionType(const Sig& sig)
|
||||
{
|
||||
const ValTypeVector& args = sig.args();
|
||||
ExprType ret = sig.ret();
|
||||
|
||||
uint32_t abiType;
|
||||
switch (ret) {
|
||||
case ExprType::F32: abiType = ArgType_Float32 << RetType_Shift; break;
|
||||
case ExprType::F64: abiType = ArgType_Double << RetType_Shift; break;
|
||||
default: MOZ_CRASH("unhandled ret type");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < args.length(); i++) {
|
||||
switch (args[i]) {
|
||||
case ValType::F32: abiType |= (ArgType_Float32 << (ArgType_Shift * (i + 1))); break;
|
||||
case ValType::F64: abiType |= (ArgType_Double << (ArgType_Shift * (i + 1))); break;
|
||||
default: MOZ_CRASH("unhandled arg type");
|
||||
}
|
||||
}
|
||||
|
||||
return ABIFunctionType(abiType);
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::Runtime::getBuiltinThunk(JSContext* cx, void* funcPtr, const Sig& sig, void** thunkPtr)
|
||||
{
|
||||
ABIFunctionType abiType = ToABIFunctionType(sig);
|
||||
#ifdef JS_SIMULATOR
|
||||
funcPtr = Simulator::RedirectNativeFunction(funcPtr, abiType);
|
||||
#endif
|
||||
return getBuiltinThunk(cx, funcPtr, abiType, thunkPtr);
|
||||
}
|
||||
|
||||
BuiltinThunk*
|
||||
wasm::Runtime::lookupBuiltin(void* pc)
|
||||
{
|
||||
size_t index;
|
||||
size_t length = builtinThunkVector_.length();
|
||||
if (!BinarySearchIf(builtinThunkVector_, 0, length, BuiltinMatcher((uint8_t*)pc), &index))
|
||||
return nullptr;
|
||||
return builtinThunkVector_[index].get();
|
||||
}
|
||||
|
||||
void
|
||||
wasm::Runtime::destroy()
|
||||
{
|
||||
builtinThunkVector_.clear();
|
||||
if (builtinThunkMap_.initialized())
|
||||
builtinThunkMap_.clear();
|
||||
}
|
||||
|
||||
BuiltinThunk::~BuiltinThunk()
|
||||
{
|
||||
executablePool->release(size, BUILTIN_THUNK_CODEKIND);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2017 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_runtime_h
|
||||
#define wasm_runtime_h
|
||||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
#include "jit/IonTypes.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
using mozilla::HashGeneric;
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace jit {
|
||||
class ExecutablePool;
|
||||
}
|
||||
|
||||
namespace wasm {
|
||||
|
||||
void*
|
||||
AddressOf(SymbolicAddress imm, jit::ABIFunctionType*);
|
||||
|
||||
struct TypedFuncPtr
|
||||
{
|
||||
void* funcPtr;
|
||||
jit::ABIFunctionType abiType;
|
||||
|
||||
TypedFuncPtr(void* funcPtr, jit::ABIFunctionType abiType)
|
||||
: funcPtr(funcPtr),
|
||||
abiType(abiType)
|
||||
{}
|
||||
|
||||
typedef TypedFuncPtr Lookup;
|
||||
static HashNumber hash(const Lookup& l) {
|
||||
return HashGeneric(l.funcPtr, uint32_t(l.abiType));
|
||||
}
|
||||
static bool match(const TypedFuncPtr& lhs, const Lookup& rhs) {
|
||||
return lhs.funcPtr == rhs.funcPtr && lhs.abiType == rhs.abiType;
|
||||
}
|
||||
};
|
||||
|
||||
typedef HashMap<TypedFuncPtr, void*, TypedFuncPtr, SystemAllocPolicy> BuiltinThunkMap;
|
||||
|
||||
struct BuiltinThunk
|
||||
{
|
||||
jit::ExecutablePool* executablePool;
|
||||
CodeRange codeRange;
|
||||
size_t size;
|
||||
uint8_t* base;
|
||||
|
||||
BuiltinThunk(uint8_t* base, size_t size, jit::ExecutablePool* executablePool,
|
||||
CallableOffsets offsets)
|
||||
: executablePool(executablePool),
|
||||
codeRange(CodeRange(CodeRange::ImportNativeExit, offsets)),
|
||||
size(size),
|
||||
base(base)
|
||||
{}
|
||||
~BuiltinThunk();
|
||||
};
|
||||
|
||||
typedef UniquePtr<BuiltinThunk> UniqueBuiltinThunk;
|
||||
typedef Vector<UniqueBuiltinThunk, 4, SystemAllocPolicy> BuiltinThunkVector;
|
||||
|
||||
// wasm::Runtime contains all the needed information for wasm that has the
|
||||
// lifetime of a JSRuntime.
|
||||
|
||||
class Runtime
|
||||
{
|
||||
BuiltinThunkMap builtinThunkMap_;
|
||||
BuiltinThunkVector builtinThunkVector_;
|
||||
|
||||
bool getBuiltinThunk(JSContext* cx, void* funcPtr, jit::ABIFunctionType type, void** thunkPtr);
|
||||
|
||||
public:
|
||||
bool init() { return builtinThunkMap_.init(); }
|
||||
void destroy();
|
||||
|
||||
bool getBuiltinThunk(JSContext* cx, void* funcPtr, const Sig& sig, void** thunkPtr);
|
||||
BuiltinThunk* lookupBuiltin(void* pc);
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // wasm_runtime_h
|
|
@ -108,7 +108,7 @@ class AutoSetHandlingSegFault
|
|||
# define R13_sig(p) ((p)->sc_r13)
|
||||
# define R14_sig(p) ((p)->sc_r14)
|
||||
# define R15_sig(p) ((p)->sc_r15)
|
||||
#elif defined(__linux__) || defined(SOLARIS)
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
# if defined(__linux__)
|
||||
# define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i])
|
||||
# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP])
|
||||
|
@ -1530,5 +1530,6 @@ js::wasm::IsPCInWasmCode(void *pc)
|
|||
if (!activation)
|
||||
return false;
|
||||
|
||||
return !!activation->compartment()->wasm.lookupCode(pc);
|
||||
return !!activation->compartment()->wasm.lookupCode(pc) ||
|
||||
!!activation->cx()->runtime()->wasm().lookupBuiltin(pc);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,15 @@ using namespace js::wasm;
|
|||
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
static void
|
||||
FinishOffsets(MacroAssembler& masm, Offsets* offsets)
|
||||
{
|
||||
// On old ARM hardware, constant pools could be inserted and they need to
|
||||
// be flushed before considering the size of the masm.
|
||||
masm.flushBuffer();
|
||||
offsets->end = masm.size();
|
||||
}
|
||||
|
||||
static void
|
||||
AssertStackAlignment(MacroAssembler& masm, uint32_t alignment, uint32_t addBeforeAssert = 0)
|
||||
{
|
||||
|
@ -331,7 +340,7 @@ wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe)
|
|||
masm.move32(Imm32(true), ReturnReg);
|
||||
masm.ret();
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
@ -498,7 +507,7 @@ wasm::GenerateImportFunction(jit::MacroAssembler& masm, const FuncImport& fi, Si
|
|||
|
||||
masm.wasmEmitTrapOutOfLineCode();
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
@ -625,7 +634,7 @@ wasm::GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint3
|
|||
|
||||
GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, &offsets);
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
@ -858,6 +867,94 @@ wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* t
|
|||
|
||||
MOZ_ASSERT(masm.framePushed() == 0);
|
||||
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
struct ABIFunctionArgs
|
||||
{
|
||||
ABIFunctionType abiType;
|
||||
size_t len;
|
||||
|
||||
explicit ABIFunctionArgs(ABIFunctionType sig)
|
||||
: abiType(ABIFunctionType(sig >> ArgType_Shift))
|
||||
{
|
||||
len = 0;
|
||||
uint32_t i = uint32_t(abiType);
|
||||
while (i) {
|
||||
i = i >> ArgType_Shift;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t length() const { return len; }
|
||||
|
||||
MIRType operator[](size_t i) const {
|
||||
MOZ_ASSERT(i < len);
|
||||
uint32_t abi = uint32_t(abiType);
|
||||
while (i--)
|
||||
abi = abi >> ArgType_Shift;
|
||||
return ToMIRType(ABIArgType(abi));
|
||||
}
|
||||
};
|
||||
|
||||
CallableOffsets
|
||||
wasm::GenerateBuiltinImportExit(MacroAssembler& masm, ABIFunctionType abiType, void* func)
|
||||
{
|
||||
masm.setFramePushed(0);
|
||||
|
||||
ABIFunctionArgs args(abiType);
|
||||
uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
|
||||
|
||||
CallableOffsets offsets;
|
||||
GenerateExitPrologue(masm, framePushed, ExitReason::ImportNative, &offsets);
|
||||
|
||||
// Copy out and convert caller arguments, if needed.
|
||||
unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
|
||||
Register scratch = ABINonArgReturnReg0;
|
||||
for (ABIArgIter<ABIFunctionArgs> i(args); !i.done(); i++) {
|
||||
if (i->argInRegister()) {
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
// Non hard-fp passes the args values in GPRs.
|
||||
if (!UseHardFpABI() && IsFloatingPointType(i.mirType())) {
|
||||
FloatRegister input = i->fpu();
|
||||
if (i.mirType() == MIRType::Float32) {
|
||||
masm.ma_vxfer(input, Register::FromCode(input.id()));
|
||||
} else if (i.mirType() == MIRType::Double) {
|
||||
uint32_t regId = input.singleOverlay().id();
|
||||
masm.ma_vxfer(input, Register::FromCode(regId), Register::FromCode(regId + 1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
|
||||
Address dst(masm.getStackPointer(), i->offsetFromArgBase());
|
||||
StackCopy(masm, i.mirType(), scratch, src, dst);
|
||||
}
|
||||
|
||||
masm.call(ImmPtr(func, ImmPtr::NoCheckToken()));
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
// x86 passes the return value on the x87 FP stack.
|
||||
Operand op(esp, 0);
|
||||
MIRType retType = ToMIRType(ABIArgType(abiType & ArgType_Mask));
|
||||
if (retType == MIRType::Float32) {
|
||||
masm.fstp32(op);
|
||||
masm.loadFloat32(op, ReturnFloat32Reg);
|
||||
} else if (retType == MIRType::Double) {
|
||||
masm.fstp(op);
|
||||
masm.loadDouble(op, ReturnDoubleReg);
|
||||
}
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
// Non hard-fp passes the return values in GPRs.
|
||||
MIRType retType = ToMIRType(ABIArgType(abiType & ArgType_Mask));
|
||||
if (!UseHardFpABI() && IsFloatingPointType(retType))
|
||||
masm.ma_vxfer(r0, r1, d0);
|
||||
#endif
|
||||
|
||||
GenerateExitEpilogue(masm, framePushed, ExitReason::ImportNative, &offsets);
|
||||
offsets.end = masm.currentOffset();
|
||||
return offsets;
|
||||
}
|
||||
|
@ -896,7 +993,7 @@ wasm::GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel)
|
|||
|
||||
GenerateExitEpilogue(masm, framePushed, ExitReason::Trap, &offsets);
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
@ -926,7 +1023,7 @@ GenerateGenericMemoryAccessTrap(MacroAssembler& masm, SymbolicAddress reporter,
|
|||
masm.call(reporter);
|
||||
masm.jump(throwLabel);
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
@ -1109,7 +1206,7 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
|
|||
# error "Unknown architecture!"
|
||||
#endif
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
@ -1147,7 +1244,7 @@ wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
|
|||
masm.mov(ImmWord(0), ReturnReg);
|
||||
masm.ret();
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
@ -1198,6 +1295,6 @@ wasm::GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel)
|
|||
|
||||
GenerateExitEpilogue(masm, 0, ExitReason::DebugTrap, &offsets);
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
FinishOffsets(masm, &offsets);
|
||||
return offsets;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
namespace jit { class MacroAssembler; class Label; }
|
||||
namespace jit {
|
||||
class MacroAssembler;
|
||||
class Label;
|
||||
enum ABIFunctionType;
|
||||
}
|
||||
|
||||
namespace wasm {
|
||||
|
||||
|
@ -43,6 +47,9 @@ GenerateImportInterpExit(jit::MacroAssembler& masm, const FuncImport& fi, uint32
|
|||
extern CallableOffsets
|
||||
GenerateImportJitExit(jit::MacroAssembler& masm, const FuncImport& fi, jit::Label* throwLabel);
|
||||
|
||||
extern CallableOffsets
|
||||
GenerateBuiltinImportExit(jit::MacroAssembler& masm, jit::ABIFunctionType abiType, void* func);
|
||||
|
||||
extern CallableOffsets
|
||||
GenerateTrapExit(jit::MacroAssembler& masm, Trap trap, jit::Label* throwLabel);
|
||||
|
||||
|
|
|
@ -18,29 +18,16 @@
|
|||
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "fdlibm.h"
|
||||
|
||||
#include "jslibmath.h"
|
||||
#include "jsmath.h"
|
||||
|
||||
#include "jit/MacroAssembler.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "wasm/WasmBaselineCompile.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmSerialize.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
|
||||
#include "vm/Debugger-inl.h"
|
||||
#include "vm/Stack-inl.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
using mozilla::IsNaN;
|
||||
using mozilla::IsPowerOfTwo;
|
||||
|
||||
void
|
||||
|
@ -67,346 +54,6 @@ Val::writePayload(uint8_t* dst) const
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
extern "C" {
|
||||
|
||||
extern MOZ_EXPORT int64_t
|
||||
__aeabi_idivmod(int, int);
|
||||
|
||||
extern MOZ_EXPORT int64_t
|
||||
__aeabi_uidivmod(int, int);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static void*
|
||||
WasmHandleExecutionInterrupt()
|
||||
{
|
||||
WasmActivation* activation = JSContext::innermostWasmActivation();
|
||||
|
||||
// wasm::Compartment requires notification when execution is interrupted in
|
||||
// the compartment. Only the innermost compartment has been interrupted;
|
||||
// enclosing compartments necessarily exited through an exit stub.
|
||||
activation->compartment()->wasm.setInterrupted(true);
|
||||
bool success = CheckForInterrupt(activation->cx());
|
||||
activation->compartment()->wasm.setInterrupted(false);
|
||||
|
||||
// Preserve the invariant that having a non-null resumePC means that we are
|
||||
// handling an interrupt.
|
||||
void* resumePC = activation->resumePC();
|
||||
activation->setResumePC(nullptr);
|
||||
|
||||
// Return the resumePC if execution can continue or null if execution should
|
||||
// jump to the throw stub.
|
||||
return success ? resumePC : nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmHandleDebugTrap()
|
||||
{
|
||||
WasmActivation* activation = JSContext::innermostWasmActivation();
|
||||
MOZ_ASSERT(activation);
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
FrameIterator iter(activation);
|
||||
MOZ_ASSERT(iter.debugEnabled());
|
||||
const CallSite* site = iter.debugTrapCallsite();
|
||||
MOZ_ASSERT(site);
|
||||
if (site->kind() == CallSite::EnterFrame) {
|
||||
if (!iter.instance()->enterFrameTrapsEnabled())
|
||||
return true;
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->setIsDebuggee();
|
||||
frame->observe(cx);
|
||||
// TODO call onEnterFrame
|
||||
JSTrapStatus status = Debugger::onEnterFrame(cx, frame);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// Ignoring forced return (JSTRAP_RETURN) -- changing code execution
|
||||
// order is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle JSTRAP_RETURN and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onEnterFrame");
|
||||
return false;
|
||||
}
|
||||
return status == JSTRAP_CONTINUE;
|
||||
}
|
||||
if (site->kind() == CallSite::LeaveFrame) {
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->updateReturnJSValue();
|
||||
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, true);
|
||||
frame->leave(cx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
Code& code = iter.instance()->code();
|
||||
MOZ_ASSERT(code.hasBreakpointTrapAtOffset(site->lineOrBytecode()));
|
||||
if (code.stepModeEnabled(frame->funcIndex())) {
|
||||
RootedValue result(cx, UndefinedValue());
|
||||
JSTrapStatus status = Debugger::onSingleStep(cx, &result);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// TODO properly handle JSTRAP_RETURN.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onSingleStep");
|
||||
return false;
|
||||
}
|
||||
if (status != JSTRAP_CONTINUE)
|
||||
return false;
|
||||
}
|
||||
if (code.hasBreakpointSite(site->lineOrBytecode())) {
|
||||
RootedValue result(cx, UndefinedValue());
|
||||
JSTrapStatus status = Debugger::onTrap(cx, &result);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// TODO properly handle JSTRAP_RETURN.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from breakpoint handler");
|
||||
return false;
|
||||
}
|
||||
if (status != JSTRAP_CONTINUE)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static WasmActivation*
|
||||
WasmHandleThrow()
|
||||
{
|
||||
JSContext* cx = TlsContext.get();
|
||||
|
||||
WasmActivation* activation = cx->wasmActivationStack();
|
||||
MOZ_ASSERT(activation);
|
||||
|
||||
// FrameIterator iterates down wasm frames in the activation starting at
|
||||
// WasmActivation::exitFP. Pass Unwind::True to pop WasmActivation::exitFP
|
||||
// once each time FrameIterator is incremented, ultimately leaving exitFP
|
||||
// null when the FrameIterator is done(). This is necessary to prevent a
|
||||
// DebugFrame from being observed again after we just called onLeaveFrame
|
||||
// (which would lead to the frame being re-added to the map of live frames,
|
||||
// right as it becomes trash).
|
||||
FrameIterator iter(activation, FrameIterator::Unwind::True);
|
||||
if (iter.done())
|
||||
return activation;
|
||||
|
||||
// Live wasm code on the stack is kept alive (in wasm::TraceActivations) by
|
||||
// marking the instance of every wasm::Frame found by FrameIterator.
|
||||
// However, as explained above, we're popping frames while iterating which
|
||||
// means that a GC during this loop could collect the code of frames whose
|
||||
// code is still on the stack. This is actually mostly fine: as soon as we
|
||||
// return to the throw stub, the entire stack will be popped as a whole,
|
||||
// returning to the C++ caller. However, we must keep the throw stub alive
|
||||
// itself which is owned by the innermost instance.
|
||||
RootedWasmInstanceObject keepAlive(cx, iter.instance()->object());
|
||||
|
||||
for (; !iter.done(); ++iter) {
|
||||
if (!iter.debugEnabled())
|
||||
continue;
|
||||
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->clearReturnJSValue();
|
||||
|
||||
// Assume JSTRAP_ERROR status if no exception is pending --
|
||||
// no onExceptionUnwind handlers must be fired.
|
||||
if (cx->isExceptionPending()) {
|
||||
JSTrapStatus status = Debugger::onExceptionUnwind(cx, frame);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// Unexpected trap return -- raising error since throw recovery
|
||||
// is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle JSTRAP_RETURN and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onExceptionUnwind");
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, false);
|
||||
if (ok) {
|
||||
// Unexpected success from the handler onLeaveFrame -- raising error
|
||||
// since throw recovery is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle success and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
|
||||
}
|
||||
frame->leave(cx);
|
||||
}
|
||||
|
||||
return activation;
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportTrap(int32_t trapIndex)
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
|
||||
MOZ_ASSERT(trapIndex < int32_t(Trap::Limit) && trapIndex >= 0);
|
||||
Trap trap = Trap(trapIndex);
|
||||
|
||||
unsigned errorNumber;
|
||||
switch (trap) {
|
||||
case Trap::Unreachable:
|
||||
errorNumber = JSMSG_WASM_UNREACHABLE;
|
||||
break;
|
||||
case Trap::IntegerOverflow:
|
||||
errorNumber = JSMSG_WASM_INTEGER_OVERFLOW;
|
||||
break;
|
||||
case Trap::InvalidConversionToInteger:
|
||||
errorNumber = JSMSG_WASM_INVALID_CONVERSION;
|
||||
break;
|
||||
case Trap::IntegerDivideByZero:
|
||||
errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
|
||||
break;
|
||||
case Trap::IndirectCallToNull:
|
||||
errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
|
||||
break;
|
||||
case Trap::IndirectCallBadSig:
|
||||
errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
|
||||
break;
|
||||
case Trap::ImpreciseSimdConversion:
|
||||
errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
|
||||
break;
|
||||
case Trap::OutOfBounds:
|
||||
errorNumber = JSMSG_WASM_OUT_OF_BOUNDS;
|
||||
break;
|
||||
case Trap::StackOverflow:
|
||||
errorNumber = JSMSG_OVER_RECURSED;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected trap");
|
||||
}
|
||||
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportOutOfBounds()
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportUnalignedAccess()
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNALIGNED_ACCESS);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
CoerceInPlace_ToInt32(MutableHandleValue val)
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
|
||||
int32_t i32;
|
||||
if (!ToInt32(cx, val, &i32))
|
||||
return false;
|
||||
val.set(Int32Value(i32));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
CoerceInPlace_ToNumber(MutableHandleValue val)
|
||||
{
|
||||
JSContext* cx = JSContext::innermostWasmActivation()->cx();
|
||||
|
||||
double dbl;
|
||||
if (!ToNumber(cx, val, &dbl))
|
||||
return false;
|
||||
val.set(DoubleValue(dbl));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
int64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
int64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(x != INT64_MIN || y != -1);
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x / y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
UDivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x / y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
ModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
int64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
int64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(x != INT64_MIN || y != -1);
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x % y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
|
||||
{
|
||||
uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
|
||||
uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
|
||||
MOZ_ASSERT(y != 0);
|
||||
return x % y;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
TruncateDoubleToInt64(double input)
|
||||
{
|
||||
// Note: INT64_MAX is not representable in double. It is actually
|
||||
// INT64_MAX + 1. Therefore also sending the failure value.
|
||||
if (input >= double(INT64_MAX) || input < double(INT64_MIN) || IsNaN(input))
|
||||
return 0x8000000000000000;
|
||||
return int64_t(input);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
TruncateDoubleToUint64(double input)
|
||||
{
|
||||
// Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
|
||||
// Therefore also sending the failure value.
|
||||
if (input >= double(UINT64_MAX) || input <= -1.0 || IsNaN(input))
|
||||
return 0x8000000000000000;
|
||||
return uint64_t(input);
|
||||
}
|
||||
|
||||
static double
|
||||
Int64ToDouble(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
|
||||
return double(x);
|
||||
}
|
||||
|
||||
static float
|
||||
Int64ToFloat32(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
|
||||
return float(x);
|
||||
}
|
||||
|
||||
static double
|
||||
Uint64ToDouble(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
|
||||
return double(x);
|
||||
}
|
||||
|
||||
static float
|
||||
Uint64ToFloat32(int32_t x_hi, uint32_t x_lo)
|
||||
{
|
||||
uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
|
||||
return float(x);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static inline void*
|
||||
FuncCast(F* pf, ABIFunctionType type)
|
||||
{
|
||||
void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
|
||||
#ifdef JS_SIMULATOR
|
||||
pv = Simulator::RedirectNativeFunction(pv, type);
|
||||
#endif
|
||||
return pv;
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode)
|
||||
{
|
||||
|
@ -432,125 +79,6 @@ wasm::IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode)
|
|||
}
|
||||
}
|
||||
|
||||
void*
|
||||
wasm::AddressOf(SymbolicAddress imm)
|
||||
{
|
||||
switch (imm) {
|
||||
case SymbolicAddress::HandleExecutionInterrupt:
|
||||
return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
|
||||
case SymbolicAddress::HandleDebugTrap:
|
||||
return FuncCast(WasmHandleDebugTrap, Args_General0);
|
||||
case SymbolicAddress::HandleThrow:
|
||||
return FuncCast(WasmHandleThrow, Args_General0);
|
||||
case SymbolicAddress::ReportTrap:
|
||||
return FuncCast(WasmReportTrap, Args_General1);
|
||||
case SymbolicAddress::ReportOutOfBounds:
|
||||
return FuncCast(WasmReportOutOfBounds, Args_General0);
|
||||
case SymbolicAddress::ReportUnalignedAccess:
|
||||
return FuncCast(WasmReportUnalignedAccess, Args_General0);
|
||||
case SymbolicAddress::CallImport_Void:
|
||||
return FuncCast(Instance::callImport_void, Args_General4);
|
||||
case SymbolicAddress::CallImport_I32:
|
||||
return FuncCast(Instance::callImport_i32, Args_General4);
|
||||
case SymbolicAddress::CallImport_I64:
|
||||
return FuncCast(Instance::callImport_i64, Args_General4);
|
||||
case SymbolicAddress::CallImport_F64:
|
||||
return FuncCast(Instance::callImport_f64, Args_General4);
|
||||
case SymbolicAddress::CoerceInPlace_ToInt32:
|
||||
return FuncCast(CoerceInPlace_ToInt32, Args_General1);
|
||||
case SymbolicAddress::CoerceInPlace_ToNumber:
|
||||
return FuncCast(CoerceInPlace_ToNumber, Args_General1);
|
||||
case SymbolicAddress::ToInt32:
|
||||
return FuncCast<int32_t (double)>(JS::ToInt32, Args_Int_Double);
|
||||
case SymbolicAddress::DivI64:
|
||||
return FuncCast(DivI64, Args_General4);
|
||||
case SymbolicAddress::UDivI64:
|
||||
return FuncCast(UDivI64, Args_General4);
|
||||
case SymbolicAddress::ModI64:
|
||||
return FuncCast(ModI64, Args_General4);
|
||||
case SymbolicAddress::UModI64:
|
||||
return FuncCast(UModI64, Args_General4);
|
||||
case SymbolicAddress::TruncateDoubleToUint64:
|
||||
return FuncCast(TruncateDoubleToUint64, Args_Int64_Double);
|
||||
case SymbolicAddress::TruncateDoubleToInt64:
|
||||
return FuncCast(TruncateDoubleToInt64, Args_Int64_Double);
|
||||
case SymbolicAddress::Uint64ToDouble:
|
||||
return FuncCast(Uint64ToDouble, Args_Double_IntInt);
|
||||
case SymbolicAddress::Uint64ToFloat32:
|
||||
return FuncCast(Uint64ToFloat32, Args_Float32_IntInt);
|
||||
case SymbolicAddress::Int64ToDouble:
|
||||
return FuncCast(Int64ToDouble, Args_Double_IntInt);
|
||||
case SymbolicAddress::Int64ToFloat32:
|
||||
return FuncCast(Int64ToFloat32, Args_Float32_IntInt);
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
case SymbolicAddress::aeabi_idivmod:
|
||||
return FuncCast(__aeabi_idivmod, Args_General2);
|
||||
case SymbolicAddress::aeabi_uidivmod:
|
||||
return FuncCast(__aeabi_uidivmod, Args_General2);
|
||||
case SymbolicAddress::AtomicCmpXchg:
|
||||
return FuncCast(atomics_cmpxchg_asm_callout, Args_General5);
|
||||
case SymbolicAddress::AtomicXchg:
|
||||
return FuncCast(atomics_xchg_asm_callout, Args_General4);
|
||||
case SymbolicAddress::AtomicFetchAdd:
|
||||
return FuncCast(atomics_add_asm_callout, Args_General4);
|
||||
case SymbolicAddress::AtomicFetchSub:
|
||||
return FuncCast(atomics_sub_asm_callout, Args_General4);
|
||||
case SymbolicAddress::AtomicFetchAnd:
|
||||
return FuncCast(atomics_and_asm_callout, Args_General4);
|
||||
case SymbolicAddress::AtomicFetchOr:
|
||||
return FuncCast(atomics_or_asm_callout, Args_General4);
|
||||
case SymbolicAddress::AtomicFetchXor:
|
||||
return FuncCast(atomics_xor_asm_callout, Args_General4);
|
||||
#endif
|
||||
case SymbolicAddress::ModD:
|
||||
return FuncCast(NumberMod, Args_Double_DoubleDouble);
|
||||
case SymbolicAddress::SinD:
|
||||
return FuncCast<double (double)>(sin, Args_Double_Double);
|
||||
case SymbolicAddress::CosD:
|
||||
return FuncCast<double (double)>(cos, Args_Double_Double);
|
||||
case SymbolicAddress::TanD:
|
||||
return FuncCast<double (double)>(tan, Args_Double_Double);
|
||||
case SymbolicAddress::ASinD:
|
||||
return FuncCast<double (double)>(fdlibm::asin, Args_Double_Double);
|
||||
case SymbolicAddress::ACosD:
|
||||
return FuncCast<double (double)>(fdlibm::acos, Args_Double_Double);
|
||||
case SymbolicAddress::ATanD:
|
||||
return FuncCast<double (double)>(fdlibm::atan, Args_Double_Double);
|
||||
case SymbolicAddress::CeilD:
|
||||
return FuncCast<double (double)>(fdlibm::ceil, Args_Double_Double);
|
||||
case SymbolicAddress::CeilF:
|
||||
return FuncCast<float (float)>(fdlibm::ceilf, Args_Float32_Float32);
|
||||
case SymbolicAddress::FloorD:
|
||||
return FuncCast<double (double)>(fdlibm::floor, Args_Double_Double);
|
||||
case SymbolicAddress::FloorF:
|
||||
return FuncCast<float (float)>(fdlibm::floorf, Args_Float32_Float32);
|
||||
case SymbolicAddress::TruncD:
|
||||
return FuncCast<double (double)>(fdlibm::trunc, Args_Double_Double);
|
||||
case SymbolicAddress::TruncF:
|
||||
return FuncCast<float (float)>(fdlibm::truncf, Args_Float32_Float32);
|
||||
case SymbolicAddress::NearbyIntD:
|
||||
return FuncCast<double (double)>(fdlibm::nearbyint, Args_Double_Double);
|
||||
case SymbolicAddress::NearbyIntF:
|
||||
return FuncCast<float (float)>(fdlibm::nearbyintf, Args_Float32_Float32);
|
||||
case SymbolicAddress::ExpD:
|
||||
return FuncCast<double (double)>(fdlibm::exp, Args_Double_Double);
|
||||
case SymbolicAddress::LogD:
|
||||
return FuncCast<double (double)>(fdlibm::log, Args_Double_Double);
|
||||
case SymbolicAddress::PowD:
|
||||
return FuncCast(ecmaPow, Args_Double_DoubleDouble);
|
||||
case SymbolicAddress::ATan2D:
|
||||
return FuncCast(ecmaAtan2, Args_Double_DoubleDouble);
|
||||
case SymbolicAddress::GrowMemory:
|
||||
return FuncCast<uint32_t (Instance*, uint32_t)>(Instance::growMemory_i32, Args_General2);
|
||||
case SymbolicAddress::CurrentMemory:
|
||||
return FuncCast<uint32_t (Instance*)>(Instance::currentMemory_i32, Args_General1);
|
||||
case SymbolicAddress::Limit:
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Bad SymbolicAddress");
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
GetCPUID()
|
||||
{
|
||||
|
@ -1139,3 +667,76 @@ DebugFrame::leave(JSContext* cx)
|
|||
observing_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(Kind kind, Offsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(0),
|
||||
end_(offsets.end),
|
||||
funcIndex_(0),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(begin_ <= end_);
|
||||
#ifdef DEBUG
|
||||
switch (kind_) {
|
||||
case Entry:
|
||||
case DebugTrap:
|
||||
case FarJumpIsland:
|
||||
case Inline:
|
||||
case Throw:
|
||||
case Interrupt:
|
||||
break;
|
||||
case Function:
|
||||
case TrapExit:
|
||||
case ImportJitExit:
|
||||
case ImportNativeExit:
|
||||
case ImportInterpExit:
|
||||
MOZ_CRASH("should use more specific constructor");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(Kind kind, CallableOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
funcIndex_(0),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
#ifdef DEBUG
|
||||
switch (kind_) {
|
||||
case TrapExit:
|
||||
case ImportJitExit:
|
||||
case ImportNativeExit:
|
||||
case ImportInterpExit:
|
||||
break;
|
||||
case Entry:
|
||||
case DebugTrap:
|
||||
case FarJumpIsland:
|
||||
case Inline:
|
||||
case Throw:
|
||||
case Interrupt:
|
||||
case Function:
|
||||
MOZ_CRASH("should use more specific constructor");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
funcIndex_(funcIndex),
|
||||
funcLineOrBytecode_(funcLineOrBytecode),
|
||||
funcBeginToNormalEntry_(offsets.normalEntry - begin_),
|
||||
kind_(Function)
|
||||
{
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
MOZ_ASSERT(offsets.normalEntry - begin_ <= UINT8_MAX);
|
||||
}
|
||||
|
|
|
@ -97,7 +97,6 @@ typedef int32_t I32x4[4];
|
|||
typedef float F32x4[4];
|
||||
|
||||
class Code;
|
||||
class CodeRange;
|
||||
class GlobalSegment;
|
||||
class Memory;
|
||||
class Module;
|
||||
|
@ -820,6 +819,120 @@ struct FuncOffsets : CallableOffsets
|
|||
}
|
||||
};
|
||||
|
||||
// A CodeRange describes a single contiguous range of code within a wasm
|
||||
// module's code segment. A CodeRange describes what the code does and, for
|
||||
// function bodies, the name and source coordinates of the function.
|
||||
|
||||
class CodeRange
|
||||
{
|
||||
public:
|
||||
enum Kind {
|
||||
Function, // function definition
|
||||
Entry, // calls into wasm from C++
|
||||
ImportJitExit, // fast-path calling from wasm into JIT code
|
||||
ImportInterpExit, // slow-path calling from wasm into C++ interp
|
||||
ImportNativeExit, // fast-path calling from wasm into a C++ native
|
||||
TrapExit, // calls C++ to report and jumps to throw stub
|
||||
DebugTrap, // calls C++ to handle debug event
|
||||
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
||||
Inline, // stub that is jumped-to within prologue/epilogue
|
||||
Throw, // special stack-unwinding stub
|
||||
Interrupt // stub executes asynchronously to interrupt wasm
|
||||
};
|
||||
|
||||
private:
|
||||
// All fields are treated as cacheable POD:
|
||||
uint32_t begin_;
|
||||
uint32_t ret_;
|
||||
uint32_t end_;
|
||||
uint32_t funcIndex_;
|
||||
uint32_t funcLineOrBytecode_;
|
||||
uint8_t funcBeginToNormalEntry_;
|
||||
Kind kind_ : 8;
|
||||
|
||||
public:
|
||||
CodeRange() = default;
|
||||
CodeRange(Kind kind, Offsets offsets);
|
||||
CodeRange(Kind kind, CallableOffsets offsets);
|
||||
CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
|
||||
|
||||
// All CodeRanges have a begin and end.
|
||||
|
||||
uint32_t begin() const {
|
||||
return begin_;
|
||||
}
|
||||
uint32_t end() const {
|
||||
return end_;
|
||||
}
|
||||
|
||||
// Other fields are only available for certain CodeRange::Kinds.
|
||||
|
||||
Kind kind() const {
|
||||
return kind_;
|
||||
}
|
||||
|
||||
bool isFunction() const {
|
||||
return kind() == Function;
|
||||
}
|
||||
bool isImportExit() const {
|
||||
return kind() == ImportJitExit || kind() == ImportInterpExit || kind() == ImportNativeExit;
|
||||
}
|
||||
bool isTrapExit() const {
|
||||
return kind() == TrapExit;
|
||||
}
|
||||
bool isInline() const {
|
||||
return kind() == Inline;
|
||||
}
|
||||
bool isThunk() const {
|
||||
return kind() == FarJumpIsland;
|
||||
}
|
||||
|
||||
// Every CodeRange except entry and inline stubs are callable and have a
|
||||
// return statement. Asynchronous frame iteration needs to know the offset
|
||||
// of the return instruction to calculate the frame pointer.
|
||||
|
||||
uint32_t ret() const {
|
||||
MOZ_ASSERT(isFunction() || isImportExit() || isTrapExit());
|
||||
return ret_;
|
||||
}
|
||||
|
||||
// Function CodeRanges have two entry points: one for normal calls (with a
|
||||
// known signature) and one for table calls (which involves dynamic
|
||||
// signature checking).
|
||||
|
||||
uint32_t funcTableEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin_;
|
||||
}
|
||||
uint32_t funcNormalEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin_ + funcBeginToNormalEntry_;
|
||||
}
|
||||
uint32_t funcIndex() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return funcIndex_;
|
||||
}
|
||||
uint32_t funcLineOrBytecode() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return funcLineOrBytecode_;
|
||||
}
|
||||
|
||||
// A sorted array of CodeRanges can be looked up via BinarySearch and PC.
|
||||
|
||||
struct PC {
|
||||
size_t offset;
|
||||
explicit PC(size_t offset) : offset(offset) {}
|
||||
bool operator==(const CodeRange& rhs) const {
|
||||
return offset >= rhs.begin() && offset < rhs.end();
|
||||
}
|
||||
bool operator<(const CodeRange& rhs) const {
|
||||
return offset < rhs.begin();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
|
||||
|
||||
// A wasm::Trap represents a wasm-defined trap that can occur during execution
|
||||
// which triggers a WebAssembly.RuntimeError. Generated code may jump to a Trap
|
||||
// symbolically, passing the bytecode offset to report as the trap offset. The
|
||||
|
@ -1019,9 +1132,6 @@ enum class SymbolicAddress
|
|||
bool
|
||||
IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode);
|
||||
|
||||
void*
|
||||
AddressOf(SymbolicAddress imm);
|
||||
|
||||
// Assumptions captures ambient state that must be the same when compiling and
|
||||
// deserializing a module for the compiled code to be valid. If it's not, then
|
||||
// the module must be recompiled from scratch.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import tempfile
|
||||
|
@ -340,6 +341,8 @@ def run_test_harness(parser, options):
|
|||
dm_args['adbPath'] = options.adb_path
|
||||
if not dm_args['host']:
|
||||
dm_args['deviceSerial'] = options.deviceSerial
|
||||
if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
|
||||
dm_args['logLevel'] = logging.DEBUG
|
||||
|
||||
try:
|
||||
dm = mozdevice.DroidADB(**dm_args)
|
||||
|
|
|
@ -4,6 +4,33 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('findbugs-exclude.xml'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('lint*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('mobile*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('ua-update.json.in'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('assets/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('omnijar/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('src/androidTest/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Testing')
|
||||
|
||||
with Files('src/test/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
for var in ('APP_NAME', 'APP_VERSION'):
|
||||
DEFINES[var] = CONFIG['MOZ_%s' % var]
|
||||
|
||||
|
|
|
@ -4,6 +4,117 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('*.java.*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('*Manifest*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('adjust-sdk-sandbox.token'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('android-services.mozbuild'):
|
||||
BUG_COMPONENT = ('Android Background Services', 'Android Sync')
|
||||
|
||||
with Files('geckoview.ddf'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
|
||||
|
||||
with Files('crashreporter/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('java/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('java/org/mozilla/gecko/activitystream/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
|
||||
|
||||
with Files('java/org/mozilla/gecko/cleanup/**'):
|
||||
BUG_COMPONENT = ('Android Background Services', 'Firefox Health Report Service')
|
||||
|
||||
with Files('java/org/mozilla/gecko/distribution/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Distributions')
|
||||
|
||||
with Files('java/org/mozilla/gecko/firstrun/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'First Run')
|
||||
|
||||
with Files('java/org/mozilla/gecko/home/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
|
||||
|
||||
with Files('java/org/mozilla/gecko/icons/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Favicon Handling')
|
||||
|
||||
with Files('java/org/mozilla/gecko/javaaddons/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('java/org/mozilla/gecko/mdns/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('java/org/mozilla/gecko/media/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
|
||||
|
||||
with Files('java/org/mozilla/gecko/mdns/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Settings and Preferences')
|
||||
|
||||
with Files('java/org/mozilla/gecko/reader/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Reader View')
|
||||
|
||||
with Files('java/org/mozilla/gecko/restrictions/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Family Friendly Browsing')
|
||||
|
||||
with Files('java/org/mozilla/gecko/telemetry/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Metrics')
|
||||
|
||||
with Files('java/org/mozilla/gecko/text/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('java/org/mozilla/gecko/webapps/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Web Apps')
|
||||
|
||||
with Files('java/org/mozilla/gecko/*LocaleManager*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Locale switching and selection')
|
||||
|
||||
with Files('java/org/mozilla/gecko/*ChromeCast*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Screencasting')
|
||||
|
||||
with Files('java/org/mozilla/gecko/*DynamicToolbar*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Graphics, Panning and Zooming')
|
||||
|
||||
with Files('java/org/mozilla/gecko/*Presentation*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Screencasting')
|
||||
|
||||
with Files('java/org/mozilla/gecko/*GuestSession*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Profile Handling')
|
||||
|
||||
with Files('locales/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('resources/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('resources/anim/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Overlays')
|
||||
|
||||
with Files('resources/raw/*favicon*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Favicon Handling')
|
||||
|
||||
with Files('resources/xml*/*preference*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Settings and Preferences')
|
||||
|
||||
with Files('resources/menu/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('resources/menu/*home*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
|
||||
|
||||
with Files('resources/menu/*activitystream*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
|
||||
|
||||
with Files('resources/menu/browsersearch_contextmenu.xml'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
|
||||
|
||||
DIRS += ['locales']
|
||||
|
||||
GENERATED_FILES += [
|
||||
|
|
|
@ -4,6 +4,13 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# NOTE: I think there are a few other possible components in this directory
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('geckoview/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
|
||||
|
||||
DIRS += ['geckoview']
|
||||
|
||||
DEFINES['AB_CD'] = CONFIG['MOZ_UI_LOCALE']
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('extensions/**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'WebExtensions: Android')
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'SessionStore.idl',
|
||||
]
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
# Only include the following system add-ons if building Aurora or Nightly
|
||||
if not CONFIG['RELEASE_OR_BETA']:
|
||||
DIRS += [
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
if not CONFIG['MOZ_ANDROID_EXCLUDE_FONTS']:
|
||||
RESOURCE_FILES.fonts += [
|
||||
'CharisSILCompact-B.ttf',
|
||||
|
|
|
@ -39,7 +39,7 @@ public class GeckoViewActivity extends Activity {
|
|||
mGeckoView.setContentListener(new MyGeckoViewContent());
|
||||
mGeckoView.setProgressListener(new MyGeckoViewProgress());
|
||||
|
||||
final GeckoProfile profile = GeckoProfile.get(getApplicationContext());
|
||||
final GeckoProfile profile = GeckoProfile.get(this);
|
||||
|
||||
GeckoThread.initMainProcess(profile, /* args */ null, /* debugging */ false);
|
||||
GeckoThread.launch();
|
||||
|
|
|
@ -4,3 +4,5 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
jar = add_java_jar('javaaddons-1.0')
|
||||
jar.sources = [
|
||||
'java/org/mozilla/javaaddons/JavaAddonInterfaceV1.java',
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
|
@ -4,6 +4,19 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# Most files are General, a few exceptions
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('DownloadNotifications.jsm'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Download Manager')
|
||||
|
||||
with Files('HomeProvider.jsm'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Data Providers')
|
||||
|
||||
with Files('geckoview/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
|
||||
|
||||
DIRS += ['geckoview']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
|
|
|
@ -4,6 +4,51 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('bouncer/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Distributions')
|
||||
|
||||
with Files('branding/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('build/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('config/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('docs/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
with Files('geckoview/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
|
||||
|
||||
with Files('geckoview/src/main/aidl/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
|
||||
|
||||
with Files('geckoview/src/main/java/org/mozilla/gecko/mozglue/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
|
||||
|
||||
with Files('geckoview_example/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
|
||||
|
||||
with Files('gradle/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('search/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Search Activity')
|
||||
|
||||
with Files('services/**'):
|
||||
BUG_COMPONENT = ('Android Background Services', 'Android Sync')
|
||||
|
||||
with Files('themes/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Theme and Visual Design')
|
||||
|
||||
with Files('thirdparty/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'General')
|
||||
|
||||
CONFIGURE_SUBST_FILES += ['installer/Makefile']
|
||||
|
||||
DIRS += [
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Android Background Services', 'Geolocation')
|
||||
|
||||
include('stumbler_sources.mozbuild')
|
||||
|
||||
stumbler_jar = add_java_jar('stumbler')
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Android Background Services', 'Build & Test')
|
||||
|
||||
TEST_DIRS += [
|
||||
'junit3',
|
||||
]
|
||||
|
|
|
@ -4,6 +4,19 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Testing')
|
||||
|
||||
with Files('chrome/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Testing')
|
||||
|
||||
with Files('junit3/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Testing')
|
||||
|
||||
# Ideally split this up, but testing catches many files
|
||||
with Files('robocop/**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Testing')
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
|
||||
|
||||
if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']:
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Testing')
|
||||
|
||||
ANDROID_APK_NAME = 'javaaddons-test'
|
||||
ANDROID_APK_PACKAGE = 'org.mozilla.javaaddons.test'
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# catch all for new files
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Testing')
|
||||
|
||||
if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']:
|
||||
TEST_DIRS += [
|
||||
'background',
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Locale Switching')
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/modules/fdlibm/src/math_private.h b/modules/fdlibm/src/math_private.h
|
||||
--- a/modules/fdlibm/src/math_private.h
|
||||
+++ b/modules/fdlibm/src/math_private.h
|
||||
@@ -33,16 +33,21 @@
|
||||
@@ -33,16 +33,23 @@
|
||||
* to dig two 32 bit words out of the 64 bit IEEE floating point
|
||||
* value. That is non-ANSI, and, moreover, the gcc instruction
|
||||
* scheduler gets it wrong. We instead use the following macros.
|
||||
|
@ -10,8 +10,10 @@ diff --git a/modules/fdlibm/src/math_private.h b/modules/fdlibm/src/math_private
|
|||
* endianness at run time.
|
||||
*/
|
||||
|
||||
+#ifdef WIN32
|
||||
+#ifndef u_int32_t
|
||||
+#define u_int32_t uint32_t
|
||||
+#endif
|
||||
+#ifndef u_int64_t
|
||||
+#define u_int64_t uint64_t
|
||||
+#endif
|
||||
+
|
||||
|
|
|
@ -38,8 +38,10 @@
|
|||
* endianness at run time.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
#ifndef u_int32_t
|
||||
#define u_int32_t uint32_t
|
||||
#endif
|
||||
#ifndef u_int64_t
|
||||
#define u_int64_t uint64_t
|
||||
#endif
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
|
||||
#include "nsXULAppAPI.h"
|
||||
|
@ -478,10 +479,11 @@ bool
|
|||
Preferences::InitStaticMembers()
|
||||
{
|
||||
#ifndef MOZ_B2G
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
|
||||
#endif
|
||||
|
||||
if (!sShutdown && !sPreferences) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIPrefService> prefService =
|
||||
do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
}
|
||||
|
|
|
@ -14,12 +14,16 @@ interface nsIURI;
|
|||
%{C++
|
||||
#include "nsIConsoleReportCollector.h"
|
||||
namespace mozilla {
|
||||
class TimeStamp;
|
||||
|
||||
namespace dom {
|
||||
class ChannelInfo;
|
||||
}
|
||||
}
|
||||
%}
|
||||
|
||||
native TimeStamp(mozilla::TimeStamp);
|
||||
|
||||
[ptr] native ChannelInfo(mozilla::dom::ChannelInfo);
|
||||
|
||||
/**
|
||||
|
@ -97,6 +101,30 @@ interface nsIInterceptedChannel : nsISupports
|
|||
[noscript]
|
||||
readonly attribute nsIConsoleReportCollector consoleReportCollector;
|
||||
|
||||
/**
|
||||
* Save the timestamps of various service worker interception phases.
|
||||
*/
|
||||
[noscript]
|
||||
void SetLaunchServiceWorkerStart(in TimeStamp aTimeStamp);
|
||||
|
||||
[noscript]
|
||||
void SetLaunchServiceWorkerEnd(in TimeStamp aTimeStamp);
|
||||
|
||||
[noscript]
|
||||
void SetDispatchFetchEventStart(in TimeStamp aTimeStamp);
|
||||
|
||||
[noscript]
|
||||
void SetDispatchFetchEventEnd(in TimeStamp aTimeStamp);
|
||||
|
||||
[noscript]
|
||||
void SetHandleFetchEventStart(in TimeStamp aTimeStamp);
|
||||
|
||||
[noscript]
|
||||
void SetHandleFetchEventEnd(in TimeStamp aTimeStamp);
|
||||
|
||||
[noscript]
|
||||
void SaveTimeStampsToUnderlyingChannel();
|
||||
|
||||
%{C++
|
||||
already_AddRefed<nsIConsoleReportCollector>
|
||||
GetConsoleReportCollector()
|
||||
|
|
|
@ -26,6 +26,15 @@ interface nsITimedChannel : nsISupports {
|
|||
[noscript] readonly attribute TimeStamp channelCreation;
|
||||
[noscript] readonly attribute TimeStamp asyncOpen;
|
||||
|
||||
// The following are only set when the request is intercepted by a service
|
||||
// worker no matter the response is synthesized.
|
||||
[noscript] attribute TimeStamp launchServiceWorkerStart;
|
||||
[noscript] attribute TimeStamp launchServiceWorkerEnd;
|
||||
[noscript] attribute TimeStamp dispatchFetchEventStart;
|
||||
[noscript] attribute TimeStamp dispatchFetchEventEnd;
|
||||
[noscript] attribute TimeStamp handleFetchEventStart;
|
||||
[noscript] attribute TimeStamp handleFetchEventEnd;
|
||||
|
||||
// The following are only set when the document is not (only) read from the
|
||||
// cache
|
||||
[noscript] readonly attribute TimeStamp domainLookupStart;
|
||||
|
@ -66,6 +75,12 @@ interface nsITimedChannel : nsISupports {
|
|||
// All following are PRTime versions of the above.
|
||||
readonly attribute PRTime channelCreationTime;
|
||||
readonly attribute PRTime asyncOpenTime;
|
||||
readonly attribute PRTime launchServiceWorkerStartTime;
|
||||
readonly attribute PRTime launchServiceWorkerEndTime;
|
||||
readonly attribute PRTime dispatchFetchEventStartTime;
|
||||
readonly attribute PRTime dispatchFetchEventEndTime;
|
||||
readonly attribute PRTime handleFetchEventStartTime;
|
||||
readonly attribute PRTime handleFetchEventEndTime;
|
||||
readonly attribute PRTime domainLookupStartTime;
|
||||
readonly attribute PRTime domainLookupEndTime;
|
||||
readonly attribute PRTime connectStartTime;
|
||||
|
|
|
@ -656,10 +656,6 @@ bool NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport = false);
|
|||
#define ABOUT_URI_FIRST_PARTY_DOMAIN \
|
||||
"about.ef2a7dd5-93bc-417f-a698-142c3116864f.mozilla"
|
||||
|
||||
// Unique first-party domain for separating null principal.
|
||||
#define NULL_PRINCIPAL_FIRST_PARTY_DOMAIN \
|
||||
"1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla"
|
||||
|
||||
/**
|
||||
* Determines whether appcache should be checked for a given URI.
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,7 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
|||
using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
|
||||
using struct nsHttpAtom from "nsHttp.h";
|
||||
using class nsHttpResponseHead from "nsHttpResponseHead.h";
|
||||
using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
@ -132,6 +133,12 @@ struct HttpChannelOpenArgs
|
|||
uint64_t contentWindowId;
|
||||
nsCString preferredAlternativeType;
|
||||
uint64_t topLevelOuterContentWindowId;
|
||||
TimeStamp launchServiceWorkerStart;
|
||||
TimeStamp launchServiceWorkerEnd;
|
||||
TimeStamp dispatchFetchEventStart;
|
||||
TimeStamp dispatchFetchEventEnd;
|
||||
TimeStamp handleFetchEventStart;
|
||||
TimeStamp handleFetchEventEnd;
|
||||
};
|
||||
|
||||
struct HttpChannelConnectArgs
|
||||
|
|
|
@ -3617,6 +3617,84 @@ HttpBaseChannel::TimingAllowCheck(nsIPrincipal *aOrigin, bool *_retval)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetLaunchServiceWorkerStart(TimeStamp* _retval) {
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mLaunchServiceWorkerStart;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) {
|
||||
mLaunchServiceWorkerStart = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetLaunchServiceWorkerEnd(TimeStamp* _retval) {
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mLaunchServiceWorkerEnd;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) {
|
||||
mLaunchServiceWorkerEnd = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetDispatchFetchEventStart(TimeStamp* _retval) {
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mDispatchFetchEventStart;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetDispatchFetchEventStart(TimeStamp aTimeStamp) {
|
||||
mDispatchFetchEventStart = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetDispatchFetchEventEnd(TimeStamp* _retval) {
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mDispatchFetchEventEnd;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetDispatchFetchEventEnd(TimeStamp aTimeStamp) {
|
||||
mDispatchFetchEventEnd = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetHandleFetchEventStart(TimeStamp* _retval) {
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mHandleFetchEventStart;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetHandleFetchEventStart(TimeStamp aTimeStamp) {
|
||||
mHandleFetchEventStart = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetHandleFetchEventEnd(TimeStamp* _retval) {
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mHandleFetchEventEnd;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetHandleFetchEventEnd(TimeStamp aTimeStamp) {
|
||||
mHandleFetchEventEnd = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) {
|
||||
*_retval = mTransactionTimings.domainLookupStart;
|
||||
|
@ -3701,6 +3779,12 @@ HttpBaseChannel::Get##name##Time(PRTime* _retval) { \
|
|||
|
||||
IMPL_TIMING_ATTR(ChannelCreation)
|
||||
IMPL_TIMING_ATTR(AsyncOpen)
|
||||
IMPL_TIMING_ATTR(LaunchServiceWorkerStart)
|
||||
IMPL_TIMING_ATTR(LaunchServiceWorkerEnd)
|
||||
IMPL_TIMING_ATTR(DispatchFetchEventStart)
|
||||
IMPL_TIMING_ATTR(DispatchFetchEventEnd)
|
||||
IMPL_TIMING_ATTR(HandleFetchEventStart)
|
||||
IMPL_TIMING_ATTR(HandleFetchEventEnd)
|
||||
IMPL_TIMING_ATTR(DomainLookupStart)
|
||||
IMPL_TIMING_ATTR(DomainLookupEnd)
|
||||
IMPL_TIMING_ATTR(ConnectStart)
|
||||
|
|
|
@ -560,6 +560,12 @@ protected:
|
|||
TimeStamp mAsyncOpenTime;
|
||||
TimeStamp mCacheReadStart;
|
||||
TimeStamp mCacheReadEnd;
|
||||
TimeStamp mLaunchServiceWorkerStart;
|
||||
TimeStamp mLaunchServiceWorkerEnd;
|
||||
TimeStamp mDispatchFetchEventStart;
|
||||
TimeStamp mDispatchFetchEventEnd;
|
||||
TimeStamp mHandleFetchEventStart;
|
||||
TimeStamp mHandleFetchEventEnd;
|
||||
// copied from the transaction before we null out mTransaction
|
||||
// so that the timing can still be queried from OnStopRequest
|
||||
TimingStruct mTransactionTimings;
|
||||
|
|
|
@ -2423,6 +2423,13 @@ HttpChannelChild::ContinueAsyncOpen()
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
openArgs.launchServiceWorkerStart() = mLaunchServiceWorkerStart;
|
||||
openArgs.launchServiceWorkerEnd() = mLaunchServiceWorkerEnd;
|
||||
openArgs.dispatchFetchEventStart() = mDispatchFetchEventStart;
|
||||
openArgs.dispatchFetchEventEnd() = mDispatchFetchEventEnd;
|
||||
openArgs.handleFetchEventStart() = mHandleFetchEventStart;
|
||||
openArgs.handleFetchEventEnd() = mHandleFetchEventEnd;
|
||||
|
||||
// This must happen before the constructor message is sent. Otherwise messages
|
||||
// from the parent could arrive quickly and be delivered to the wrong event
|
||||
// target.
|
||||
|
|
|
@ -132,7 +132,13 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
|
|||
a.suspendAfterSynthesizeResponse(),
|
||||
a.allowStaleCacheContent(), a.contentTypeHint(),
|
||||
a.channelId(), a.contentWindowId(), a.preferredAlternativeType(),
|
||||
a.topLevelOuterContentWindowId());
|
||||
a.topLevelOuterContentWindowId(),
|
||||
a.launchServiceWorkerStart(),
|
||||
a.launchServiceWorkerEnd(),
|
||||
a.dispatchFetchEventStart(),
|
||||
a.dispatchFetchEventEnd(),
|
||||
a.handleFetchEventStart(),
|
||||
a.handleFetchEventEnd());
|
||||
}
|
||||
case HttpChannelCreationArgs::THttpChannelConnectArgs:
|
||||
{
|
||||
|
@ -331,7 +337,13 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
|
|||
const uint64_t& aChannelId,
|
||||
const uint64_t& aContentWindowId,
|
||||
const nsCString& aPreferredAlternativeType,
|
||||
const uint64_t& aTopLevelOuterContentWindowId)
|
||||
const uint64_t& aTopLevelOuterContentWindowId,
|
||||
const TimeStamp& aLaunchServiceWorkerStart,
|
||||
const TimeStamp& aLaunchServiceWorkerEnd,
|
||||
const TimeStamp& aDispatchFetchEventStart,
|
||||
const TimeStamp& aDispatchFetchEventEnd,
|
||||
const TimeStamp& aHandleFetchEventStart,
|
||||
const TimeStamp& aHandleFetchEventEnd)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
|
||||
if (!uri) {
|
||||
|
@ -540,6 +552,13 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
|
|||
mChannel->SetInitialRwin(aInitialRwin);
|
||||
mChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
|
||||
|
||||
mChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
|
||||
mChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
|
||||
mChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
|
||||
mChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
|
||||
mChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
|
||||
mChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
|
||||
|
||||
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
|
||||
do_QueryObject(mChannel);
|
||||
nsCOMPtr<nsIApplicationCacheService> appCacheService =
|
||||
|
|
|
@ -149,7 +149,13 @@ protected:
|
|||
const uint64_t& aChannelId,
|
||||
const uint64_t& aContentWindowId,
|
||||
const nsCString& aPreferredAlternativeType,
|
||||
const uint64_t& aTopLevelOuterContentWindowId);
|
||||
const uint64_t& aTopLevelOuterContentWindowId,
|
||||
const TimeStamp& aLaunchServiceWorkerStart,
|
||||
const TimeStamp& aLaunchServiceWorkerEnd,
|
||||
const TimeStamp& aDispatchFetchEventStart,
|
||||
const TimeStamp& aDispatchFetchEventEnd,
|
||||
const TimeStamp& aHandleFetchEventStart,
|
||||
const TimeStamp& aHandleFetchEventEnd);
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvSetPriority(const int16_t& priority) override;
|
||||
virtual mozilla::ipc::IPCResult RecvSetClassOfService(const uint32_t& cos) override;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nsInputStreamPump.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "HttpChannelChild.h"
|
||||
#include "nsHttpResponseHead.h"
|
||||
|
@ -131,6 +132,40 @@ InterceptedChannelBase::SetReleaseHandle(nsISupports* aHandle)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelBase::SaveTimeStampsToUnderlyingChannel()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIChannel> underlyingChannel;
|
||||
nsresult rv = GetChannel(getter_AddRefs(underlyingChannel));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsITimedChannel> timedChannel =
|
||||
do_QueryInterface(underlyingChannel);
|
||||
MOZ_ASSERT(timedChannel);
|
||||
|
||||
rv = timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<nsIURI>
|
||||
InterceptedChannelBase::SecureUpgradeChannelURI(nsIChannel* aChannel)
|
||||
|
|
|
@ -48,6 +48,13 @@ protected:
|
|||
MOZ_MUST_USE nsresult DoSynthesizeHeader(const nsACString& aName,
|
||||
const nsACString& aValue);
|
||||
|
||||
TimeStamp mLaunchServiceWorkerStart;
|
||||
TimeStamp mLaunchServiceWorkerEnd;
|
||||
TimeStamp mDispatchFetchEventStart;
|
||||
TimeStamp mDispatchFetchEventEnd;
|
||||
TimeStamp mHandleFetchEventStart;
|
||||
TimeStamp mHandleFetchEventEnd;
|
||||
|
||||
virtual ~InterceptedChannelBase();
|
||||
public:
|
||||
explicit InterceptedChannelBase(nsINetworkInterceptController* aController);
|
||||
|
@ -62,6 +69,50 @@ public:
|
|||
NS_IMETHOD GetConsoleReportCollector(nsIConsoleReportCollector** aCollectorOut) override;
|
||||
NS_IMETHOD SetReleaseHandle(nsISupports* aHandle) override;
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) override
|
||||
{
|
||||
mLaunchServiceWorkerStart = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) override
|
||||
{
|
||||
mLaunchServiceWorkerEnd = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDispatchFetchEventStart(TimeStamp aTimeStamp) override
|
||||
{
|
||||
mDispatchFetchEventStart = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDispatchFetchEventEnd(TimeStamp aTimeStamp) override
|
||||
{
|
||||
mDispatchFetchEventEnd = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetHandleFetchEventStart(TimeStamp aTimeStamp) override
|
||||
{
|
||||
mHandleFetchEventStart = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetHandleFetchEventEnd(TimeStamp aTimeStamp) override
|
||||
{
|
||||
mHandleFetchEventEnd = aTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP SaveTimeStampsToUnderlyingChannel() override;
|
||||
|
||||
static already_AddRefed<nsIURI>
|
||||
SecureUpgradeChannelURI(nsIChannel* aChannel);
|
||||
};
|
||||
|
|
|
@ -582,6 +582,90 @@ NullHttpChannel::GetAsyncOpen(mozilla::TimeStamp *aAsyncOpen)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetLaunchServiceWorkerStart(mozilla::TimeStamp *_retval)
|
||||
{
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetLaunchServiceWorkerStart(mozilla::TimeStamp aTimeStamp)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetLaunchServiceWorkerEnd(mozilla::TimeStamp *_retval)
|
||||
{
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetLaunchServiceWorkerEnd(mozilla::TimeStamp aTimeStamp)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetDispatchFetchEventStart(mozilla::TimeStamp *_retval)
|
||||
{
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetDispatchFetchEventStart(mozilla::TimeStamp aTimeStamp)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetDispatchFetchEventEnd(mozilla::TimeStamp *_retval)
|
||||
{
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetDispatchFetchEventEnd(mozilla::TimeStamp aTimeStamp)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetHandleFetchEventStart(mozilla::TimeStamp *_retval)
|
||||
{
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetHandleFetchEventStart(mozilla::TimeStamp aTimeStamp)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetHandleFetchEventEnd(mozilla::TimeStamp *_retval)
|
||||
{
|
||||
MOZ_ASSERT(_retval);
|
||||
*_retval = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetHandleFetchEventEnd(mozilla::TimeStamp aTimeStamp)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetDomainLookupStart(mozilla::TimeStamp *aDomainLookupStart)
|
||||
{
|
||||
|
@ -772,6 +856,12 @@ NullHttpChannel::Get##name##Time(PRTime* _retval) { \
|
|||
|
||||
IMPL_TIMING_ATTR(ChannelCreation)
|
||||
IMPL_TIMING_ATTR(AsyncOpen)
|
||||
IMPL_TIMING_ATTR(LaunchServiceWorkerStart)
|
||||
IMPL_TIMING_ATTR(LaunchServiceWorkerEnd)
|
||||
IMPL_TIMING_ATTR(DispatchFetchEventStart)
|
||||
IMPL_TIMING_ATTR(DispatchFetchEventEnd)
|
||||
IMPL_TIMING_ATTR(HandleFetchEventStart)
|
||||
IMPL_TIMING_ATTR(HandleFetchEventEnd)
|
||||
IMPL_TIMING_ATTR(DomainLookupStart)
|
||||
IMPL_TIMING_ATTR(DomainLookupEnd)
|
||||
IMPL_TIMING_ATTR(ConnectStart)
|
||||
|
|
|
@ -5367,6 +5367,18 @@ nsHttpChannel::SetupReplacementChannel(nsIURI *newURI,
|
|||
}
|
||||
}
|
||||
|
||||
if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
|
||||
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
|
||||
if (timedChannel) {
|
||||
timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
|
||||
timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
|
||||
timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
|
||||
timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
|
||||
timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
|
||||
timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ EXPORTS += [
|
|||
'nsSAXXMLReader.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsSAXAttributes.cpp',
|
||||
'nsSAXLocator.cpp',
|
||||
'nsSAXXMLReader.cpp',
|
||||
|
|
|
@ -146,7 +146,7 @@ android-api-15-gradle/opt:
|
|||
- name: public/android/maven
|
||||
path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
|
||||
type: directory
|
||||
- name: public/android/geckoview_example.apk
|
||||
- name: public/build/geckoview_example.apk
|
||||
path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/geckoview_example-withGeckoBinaries.apk
|
||||
type: file
|
||||
- name: public/build
|
||||
|
|
|
@ -241,6 +241,7 @@ android-opt-tests:
|
|||
android-gradle-tests:
|
||||
- mochitest-chrome
|
||||
- robocop
|
||||
- geckoview
|
||||
|
||||
android-x86-tests:
|
||||
- mochitest-chrome
|
||||
|
|
|
@ -606,6 +606,7 @@ mochitest-clipboard:
|
|||
suite: mochitest/clipboard
|
||||
treeherder-symbol: tc-M(cl)
|
||||
loopback-video: true
|
||||
docker-image: {"in-tree": "desktop1604-test"}
|
||||
instance-size: xlarge
|
||||
e10s:
|
||||
by-test-platform:
|
||||
|
@ -914,6 +915,7 @@ mochitest-style:
|
|||
suite: mochitest/plain-style
|
||||
treeherder-symbol: tc-M(s)
|
||||
loopback-video: true
|
||||
docker-image: {"in-tree": "desktop1604-test"}
|
||||
e10s: both
|
||||
run-on-projects:
|
||||
by-test-platform:
|
||||
|
@ -936,6 +938,7 @@ mochitest-chrome-style:
|
|||
suite: mochitest/chrome-style
|
||||
treeherder-symbol: tc-M(cs)
|
||||
loopback-video: true
|
||||
docker-image: {"in-tree": "desktop1604-test"}
|
||||
run-on-projects:
|
||||
by-test-platform:
|
||||
linux64-stylo/.*: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
|
||||
|
@ -1096,6 +1099,21 @@ robocop:
|
|||
extra-options:
|
||||
- --test-suite=robocop
|
||||
|
||||
geckoview:
|
||||
description: "Geckoview run"
|
||||
suite: geckoview
|
||||
treeherder-symbol: tc(gv)
|
||||
instance-size: xlarge
|
||||
loopback-video: true
|
||||
e10s: false
|
||||
mozharness:
|
||||
script: android_emulator_unittest.py
|
||||
no-read-buildbot-config: true
|
||||
config:
|
||||
- android/androidarm_4_3.py
|
||||
extra-options:
|
||||
- --test-suite=geckoview
|
||||
|
||||
talos-chrome:
|
||||
description: "Talos chrome"
|
||||
suite: talos
|
||||
|
|
|
@ -341,7 +341,10 @@ def set_target(config, tests):
|
|||
if build_platform.startswith('macosx'):
|
||||
target = 'target.dmg'
|
||||
elif build_platform.startswith('android'):
|
||||
target = 'target.apk'
|
||||
if 'geckoview' in test['test-name']:
|
||||
target = 'geckoview_example.apk'
|
||||
else:
|
||||
target = 'target.apk'
|
||||
elif build_platform.startswith('win'):
|
||||
target = 'firefox-{}.en-US.{}.zip'.format(
|
||||
get_firefox_version(),
|
||||
|
|
|
@ -7,6 +7,7 @@ from argparse import ArgumentParser, SUPPRESS
|
|||
from distutils.util import strtobool
|
||||
from itertools import chain
|
||||
from urlparse import urlparse
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
@ -957,6 +958,9 @@ class AndroidArguments(ArgumentContainer):
|
|||
device_args['port'] = options.devicePort
|
||||
elif options.deviceSerial:
|
||||
device_args['deviceSerial'] = options.deviceSerial
|
||||
|
||||
if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
|
||||
device_args['logLevel'] = logging.DEBUG
|
||||
options.dm = DroidADB(**device_args)
|
||||
|
||||
if not options.remoteTestRoot:
|
||||
|
|
|
@ -61,6 +61,7 @@ TEST_HARNESS_FILES.testing.mochitest += [
|
|||
'nested_setup.js',
|
||||
'pywebsocket_wrapper.py',
|
||||
'redirect.html',
|
||||
'rungeckoview.py',
|
||||
'runrobocop.py',
|
||||
'runtests.py',
|
||||
'runtestsremote.py',
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
import posixpath
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import traceback
|
||||
from optparse import OptionParser
|
||||
|
||||
import mozcrash
|
||||
import mozdevice
|
||||
import mozlog
|
||||
from mozprofile import Profile
|
||||
|
||||
|
||||
class GeckoviewOptions(OptionParser):
|
||||
def __init__(self):
|
||||
OptionParser.__init__(self)
|
||||
self.add_option("--utility-path",
|
||||
action="store", type="string", dest="utility_path",
|
||||
default=None,
|
||||
help="absolute path to directory containing utility programs")
|
||||
self.add_option("--symbols-path",
|
||||
action="store", type="string", dest="symbols_path",
|
||||
default=None,
|
||||
help="absolute path to directory containing breakpad symbols, \
|
||||
or the URL of a zip file containing symbols")
|
||||
self.add_option("--appname",
|
||||
action="store", type="string", dest="app",
|
||||
default="org.mozilla.geckoview_example",
|
||||
help="geckoview_example package name")
|
||||
self.add_option("--deviceIP",
|
||||
action="store", type="string", dest="deviceIP",
|
||||
default=None,
|
||||
help="ip address of remote device to test")
|
||||
self.add_option("--deviceSerial",
|
||||
action="store", type="string", dest="deviceSerial",
|
||||
default=None,
|
||||
help="serial ID of remote device to test")
|
||||
self.add_option("--adbpath",
|
||||
action="store", type="string", dest="adbPath",
|
||||
default="adb",
|
||||
help="Path to adb binary.")
|
||||
self.add_option("--remoteTestRoot",
|
||||
action="store", type="string", dest="remoteTestRoot",
|
||||
default=None,
|
||||
help="remote directory to use as test root \
|
||||
(eg. /mnt/sdcard/tests or /data/local/tests)")
|
||||
|
||||
|
||||
class GeckoviewTestRunner:
|
||||
"""
|
||||
A quick-and-dirty test harness to verify the geckoview_example
|
||||
app starts without crashing.
|
||||
"""
|
||||
|
||||
def __init__(self, log, dm, options):
|
||||
self.log = log
|
||||
self.dm = dm
|
||||
self.options = options
|
||||
self.appname = self.options.app.split('/')[-1]
|
||||
self.logcat = None
|
||||
self.build_profile()
|
||||
self.log.debug("options=%s" % vars(options))
|
||||
|
||||
def build_profile(self):
|
||||
test_root = self.dm.deviceRoot
|
||||
self.remote_profile = posixpath.join(test_root, 'gv-profile')
|
||||
self.dm.mkDirs(posixpath.join(self.remote_profile, "x"))
|
||||
profile = Profile()
|
||||
self.dm.pushDir(profile.profile, self.remote_profile)
|
||||
self.log.debug("profile %s -> %s" %
|
||||
(str(profile.profile), str(self.remote_profile)))
|
||||
|
||||
def installed(self):
|
||||
"""
|
||||
geckoview_example installed
|
||||
"""
|
||||
installed = self.dm.shellCheckOutput(['pm', 'list', 'packages', self.appname])
|
||||
if self.appname not in installed:
|
||||
return (False, "%s not installed" % self.appname)
|
||||
return (True, "%s installed" % self.appname)
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
geckoview_example starts
|
||||
"""
|
||||
try:
|
||||
self.dm.stopApplication(self.appname)
|
||||
self.dm.recordLogcat()
|
||||
cmd = ['am', 'start', '-a', 'android.intent.action.MAIN', '-n',
|
||||
'org.mozilla.geckoview_example/org.mozilla.geckoview_example.GeckoViewActivity',
|
||||
'--es', 'args', '-profile %s' % self.remote_profile]
|
||||
env = {}
|
||||
env["MOZ_CRASHREPORTER"] = 1
|
||||
env["MOZ_CRASHREPORTER_NO_REPORT"] = 1
|
||||
env["XPCOM_DEBUG_BREAK"] = "stack"
|
||||
env["DISABLE_UNSAFE_CPOW_WARNINGS"] = 1
|
||||
env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = 1
|
||||
env["MOZ_IN_AUTOMATION"] = 1
|
||||
env["R_LOG_VERBOSE"] = 1
|
||||
env["R_LOG_LEVEL"] = 6
|
||||
env["R_LOG_DESTINATION"] = "stderr"
|
||||
i = 0
|
||||
for key, value in env.iteritems():
|
||||
cmd.append("--es")
|
||||
cmd.append("env%d" % i)
|
||||
cmd.append("%s=%s" % (key, str(value)))
|
||||
i = i + 1
|
||||
self.dm.shellCheckOutput(cmd)
|
||||
except mozdevice.DMError:
|
||||
return (False, "Exception during %s startup" % self.appname)
|
||||
return (True, "%s started" % self.appname)
|
||||
|
||||
def started(self):
|
||||
"""
|
||||
startup logcat messages
|
||||
"""
|
||||
expected = [
|
||||
"zerdatime",
|
||||
"Displayed %s/.GeckoViewActivity" % self.appname
|
||||
]
|
||||
# wait up to 60 seconds for startup
|
||||
for wait_time in xrange(60):
|
||||
time.sleep(1)
|
||||
self.logcat = self.dm.getLogcat()
|
||||
for line in self.logcat:
|
||||
for e in expected:
|
||||
if e in line:
|
||||
self.log.debug(line.strip())
|
||||
expected.remove(e)
|
||||
if len(expected) == 0:
|
||||
return (True, "All expected logcat messages found")
|
||||
for e in expected:
|
||||
self.log.error("missing from logcat: '%s'" % e)
|
||||
return (False, "'%s' not found in logcat" % expected[0])
|
||||
|
||||
def run_tests(self):
|
||||
"""
|
||||
Run simple tests to verify that the geckoview_example app starts.
|
||||
"""
|
||||
all_tests = [self.installed, self.start, self.started]
|
||||
self.log.suite_start(all_tests)
|
||||
pass_count = 0
|
||||
fail_count = 0
|
||||
for test in all_tests:
|
||||
self.test_name = test.__doc__.strip()
|
||||
self.log.test_start(self.test_name)
|
||||
|
||||
expected = 'PASS'
|
||||
(passed, message) = test()
|
||||
if passed:
|
||||
pass_count = pass_count + 1
|
||||
else:
|
||||
fail_count = fail_count + 1
|
||||
status = 'PASS' if passed else 'FAIL'
|
||||
|
||||
self.log.test_end(self.test_name, status, expected, message)
|
||||
|
||||
self.log.info("Passed: %d" % pass_count)
|
||||
self.log.info("Failed: %d" % fail_count)
|
||||
self.log.suite_end()
|
||||
|
||||
return 0 if passed else 1
|
||||
|
||||
def check_for_crashes(self):
|
||||
if self.logcat:
|
||||
if mozcrash.check_for_java_exception(self.logcat, self.test_name):
|
||||
return True
|
||||
symbols_path = self.options.symbols_path
|
||||
try:
|
||||
dump_dir = tempfile.mkdtemp()
|
||||
remote_dir = posixpath.join(self.remote_profile, 'minidumps')
|
||||
if not self.dm.dirExists(remote_dir):
|
||||
# If crash reporting is enabled (MOZ_CRASHREPORTER=1), the
|
||||
# minidumps directory is automatically created when the app
|
||||
# (first) starts, so its lack of presence is a hint that
|
||||
# something went wrong.
|
||||
print "Automation Error: No crash directory (%s) found on remote device" % \
|
||||
remote_dir
|
||||
# Whilst no crash was found, the run should still display as a failure
|
||||
return True
|
||||
self.dm.getDirectory(remote_dir, dump_dir)
|
||||
crashed = mozcrash.log_crashes(self.log, dump_dir, symbols_path, test=self.test_name)
|
||||
finally:
|
||||
try:
|
||||
shutil.rmtree(dump_dir)
|
||||
except:
|
||||
self.log.warn("unable to remove directory: %s" % dump_dir)
|
||||
return crashed
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Cleanup at end of job run.
|
||||
"""
|
||||
self.log.debug("Cleaning up...")
|
||||
self.dm.stopApplication(self.appname)
|
||||
crashed = self.check_for_crashes()
|
||||
self.dm.removeDir(self.remote_profile)
|
||||
self.log.debug("Cleanup complete.")
|
||||
return crashed
|
||||
|
||||
|
||||
def run_test_harness(log, parser, options):
|
||||
device_args = {'deviceRoot': options.remoteTestRoot}
|
||||
device_args['adbPath'] = options.adbPath
|
||||
if options.deviceIP:
|
||||
device_args['host'] = options.deviceIP
|
||||
device_args['port'] = options.devicePort
|
||||
elif options.deviceSerial:
|
||||
device_args['deviceSerial'] = options.deviceSerial
|
||||
device_args['packageName'] = options.app
|
||||
dm = mozdevice.DroidADB(**device_args)
|
||||
|
||||
runner = GeckoviewTestRunner(log, dm, options)
|
||||
result = -1
|
||||
try:
|
||||
result = runner.run_tests()
|
||||
except KeyboardInterrupt:
|
||||
log.info("rungeckoview.py | Received keyboard interrupt")
|
||||
result = -1
|
||||
except:
|
||||
traceback.print_exc()
|
||||
log.error(
|
||||
"rungeckoview.py | Received unexpected exception while running tests")
|
||||
result = 1
|
||||
finally:
|
||||
try:
|
||||
crashed = runner.cleanup()
|
||||
if not result:
|
||||
result = crashed
|
||||
except mozdevice.DMError:
|
||||
# ignore device error while cleaning up
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
def main(args=sys.argv[1:]):
|
||||
parser = GeckoviewOptions()
|
||||
mozlog.commandline.add_logging_group(parser)
|
||||
options, args = parser.parse_args()
|
||||
if args:
|
||||
print >>sys.stderr, """Usage: %s""" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
log = mozlog.commandline.setup_logging("rungeckoview", options,
|
||||
{"tbpl": sys.stdout})
|
||||
return run_test_harness(log, parser, options)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
|
@ -356,6 +356,14 @@ config = {
|
|||
"--startup-timeout=300",
|
||||
],
|
||||
},
|
||||
"geckoview": {
|
||||
"run_filename": "rungeckoview.py",
|
||||
"testsdir": "mochitest",
|
||||
"options": [
|
||||
"--utility-path=%(utility_path)s",
|
||||
"--symbols-path=%(symbols_path)s",
|
||||
],
|
||||
},
|
||||
|
||||
}, # end suite_definitions
|
||||
"download_minidump_stackwalk": True,
|
||||
|
|
|
@ -94,6 +94,12 @@ TinderBoxPrintRe = {
|
|||
'fail_group': "Failed",
|
||||
'known_fail_group': "Skipped",
|
||||
},
|
||||
"geckoview_summary": {
|
||||
'regex': re.compile(r'''(Passed|Failed): (\d+)'''),
|
||||
'pass_group': "Passed",
|
||||
'fail_group': "Failed",
|
||||
'known_fail_group': None,
|
||||
},
|
||||
|
||||
"harness_error": {
|
||||
'full_regex': re.compile(r"(?:TEST-UNEXPECTED-FAIL|PROCESS-CRASH) \| .* \| (application crashed|missing output line for total leaks!|negative leaks caught!|\d+ bytes leaked)"),
|
||||
|
|
|
@ -439,6 +439,7 @@ You can set this by:
|
|||
'mochitest-plain-clipboard': 'mochitest',
|
||||
'mochitest-plain-gpu': 'mochitest',
|
||||
'mochitest-gl': 'mochitest',
|
||||
'geckoview': 'mochitest',
|
||||
'jsreftest': 'reftest',
|
||||
'crashtest': 'reftest',
|
||||
'reftest-debug': 'reftest',
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче