--- a/devtools/client/inspector/layout/test/browser.ini
+++ b/devtools/client/inspector/layout/test/browser.ini
@@ -19,11 +19,12 @@ support-files =
[browser_layout_editablemodel_bluronclick.js]
[browser_layout_editablemodel_border.js]
[browser_layout_editablemodel_stylerules.js]
[browser_layout_guides.js]
[browser_layout_rotate-labels-on-sides.js]
[browser_layout_sync.js]
[browser_layout_tooltips.js]
[browser_layout_update-after-navigation.js]
-[browser_layout_update-after-reload.js]
+# [browser_layout_update-after-reload.js]
+# Disabled for too many intermittent failures (bug 1287745)
# [browser_layout_update-in-iframes.js]
# Bug 1020038 layout-view updates for iframe elements changes
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10779,17 +10779,18 @@ nsDocShell::DoURILoad(nsIURI* aURI,
return NS_OK;
}
}
bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN;
// only inherit if we have a triggeringPrincipal
bool inherit = false;
- // Get triggeringPrincipal. This code should be updated by bug 1181370.
+ // Getting the right triggeringPrincipal needs to be updated and is only
+ // ready for use once bug 1182569 landed.
// Until then, we cannot rely on the triggeringPrincipal for TYPE_DOCUMENT
// or TYPE_SUBDOCUMENT loads. Notice the triggeringPrincipal falls back to
// systemPrincipal below.
nsCOMPtr<nsIPrincipal> triggeringPrincipal = do_QueryInterface(aOwner);
if (triggeringPrincipal) {
inherit = nsContentUtils::ChannelShouldInheritPrincipal(
triggeringPrincipal,
aURI,
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 1</b><br/>
+<a href="#"" id="testlink" onclick="parent.frames[1].frames[0].location='http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html'">click me</a>
+
+<script type="application/javascript">
+ // make sure to set document.domain to the same domain as the subframe
+ window.onload = function() {
+ document.domain = 'mochi.test';
+ };
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(event) {
+ // make sure to get the right start command, otherwise
+ // let the parent know and fail the test
+ if (event.data.start !== 'startTest') {
+ window.removeEventListener("message", receiveMessage, false);
+ window.parent.postMessage({triggeringPrincipalURI: 'false'}, '*');
+ }
+ // click the link to navigate the subframe
+ document.getElementById('testlink').click();
+ }
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 2</b><br/>
+<iframe src="http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe.html"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset='utf-8'></head>
+<body>
+<b>Sub Frame 2</b><br/>
+<script type='application/javascript'>
+ // make sure to set document.domain to same domain as frame 1
+ window.onload = function() {
+ document.domain = 'mochi.test';
+ // let Frame 1 know that we are ready to run the test
+ window.parent.parent.frames[0].postMessage({start: 'startTest'}, '*');
+ };
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="checkResults()">
+<b>Sub Frame 2 Navigated</b><br/>
+
+<script type='application/javascript'>
+ function checkResults() {
+ // query the uri of the loadingPrincipal and the TriggeringPrincipal and pass
+ // that information on to the parent for verification.
+ var channel = SpecialPowers.wrap(document).docShell.currentDocumentChannel;
+ var triggeringPrincipalURI = channel.loadInfo.triggeringPrincipal.URI.asciiSpec;
+ var loadingPrincipalURI = channel.loadInfo.loadingPrincipal.URI.asciiSpec;
+ var referrerURI = document.referrer;
+ window.parent.parent.postMessage({triggeringPrincipalURI,
+ loadingPrincipalURI,
+ referrerURI}, '*');
+ }
+</script>
+</body>
+</html>
--- a/docshell/test/navigation/mochitest.ini
+++ b/docshell/test/navigation/mochitest.ini
@@ -17,16 +17,20 @@ support-files =
frame1.html
frame2.html
frame3.html
goback.html
iframe.html
navigate.html
open.html
parent.html
+ file_triggeringprincipal_frame_1.html
+ file_triggeringprincipal_frame_2.html
+ file_triggeringprincipal_subframe.html
+ file_triggeringprincipal_subframe_nav.html
[test_bug13871.html]
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' #RANDOM # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures
[test_bug270414.html]
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == "android" # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures
[test_bug278916.html]
[test_bug279495.html]
[test_bug344861.html]
@@ -43,8 +47,9 @@ skip-if = buildapp == 'b2g'
[test_popup-navigates-children.html]
skip-if = buildapp == 'b2g' # b2g(Needs multiple window.open support, also uses docshelltreenode) b2g-debug(Needs multiple window.open support, also uses docshelltreenode) b2g-desktop(Needs multiple window.open support, also uses docshelltreenode)
[test_reserved.html]
skip-if = (buildapp == 'b2g' && debug) || (toolkit == 'android') || (debug && e10s) #too slow on Android 4.3 aws only; bug 1030403; bug 1263213 for debug e10s
[test_sessionhistory.html]
skip-if = (buildapp == 'b2g' && debug) || toolkit == 'android' #RANDOM # b2g-debug(Perma-orange on debug emulator builds) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
[test_sibling-matching-parent.html]
[test_sibling-off-domain.html]
+[test_triggeringprincipal_frame_nav.html]
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_frame_nav.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1181370 - Test triggeringPrincipal for iframe navigations</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe1"></iframe>
+<iframe style="width:100%;" id="testframe2"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ *
+ * +------------------------------------+
+ * | +----------+ +--------------+ |
+ * | | Frame 1 | | Frame 2 | |
+ * | +----------+ | | |
+ * | | +----------+ | |
+ * | | | Subframe | | |
+ * | | +----------+ | |
+ * | +--------------+ |
+ * +------------------------------------+
+ *
+ * Frame1: test1.mochi.test
+ * Frame2: test2.mochi.test
+ * Subframe: test2.mochi.test
+ *
+ * (*) Frame1 and Subframe set their document.domain to mochi.test
+ * (*) Frame1 navigates the Subframe
+ * (*) TriggeringPrincipal for the Subframe navigation should be
+ * ==> test1.mochi.test
+ * (*) LoadingPrincipal for the Subframe navigation should be
+ * ==> test2.mochi.test
+ */
+
+const BASEURL1 = "http://test1.mochi.test:8888/tests/docshell/test/navigation/";
+const BASEURL2 = "http://test2.mochi.test:8888/tests/docshell/test/navigation/";
+const TRIGGERINGPRINCIPALURI = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+const LOADINGPRINCIPALURI = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+ is(event.data.triggeringPrincipalURI, TRIGGERINGPRINCIPALURI,
+ "TriggeringPrincipal should be the navigating iframe (Frame 1)");
+ is(event.data.loadingPrincipalURI, LOADINGPRINCIPALURI,
+ "LoadingPrincipal should be the enclosing iframe (Frame 2)");
+ is(event.data.referrerURI, TRIGGERINGPRINCIPALURI,
+ "Referrer and TriggeringPrincipal should be identical (Frame 1)");
+
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+var frame1 = document.getElementById("testframe1");
+frame1.src = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+
+var frame2 = document.getElementById("testframe2");
+frame2.src = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+</script>
+</body>
+</html>
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -696,18 +696,18 @@ Element::Scroll(const CSSIntPoint& aScro
sf->ScrollToCSSPixels(aScroll, scrollMode);
}
}
void
Element::Scroll(double aXScroll, double aYScroll)
{
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
- CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
- mozilla::ToZeroIfNonfinite(aYScroll));
+ auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+ mozilla::ToZeroIfNonfinite(aYScroll));
Scroll(scrollPos, ScrollOptions());
}
void
Element::Scroll(const ScrollToOptions& aOptions)
{
nsIScrollableFrame *sf = GetScrollFrame();
@@ -736,18 +736,18 @@ Element::ScrollTo(const ScrollToOptions&
}
void
Element::ScrollBy(double aXScrollDif, double aYScrollDif)
{
nsIScrollableFrame *sf = GetScrollFrame();
if (sf) {
CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
- scrollPos += CSSIntPoint(mozilla::ToZeroIfNonfinite(aXScrollDif),
- mozilla::ToZeroIfNonfinite(aYScrollDif));
+ scrollPos += CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScrollDif),
+ mozilla::ToZeroIfNonfinite(aYScrollDif));
Scroll(scrollPos, ScrollOptions());
}
}
void
Element::ScrollBy(const ScrollToOptions& aOptions)
{
nsIScrollableFrame *sf = GetScrollFrame();
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1327,22 +1327,27 @@ Navigator::SendBeacon(const nsAString& a
if (NS_FAILED(rv) || isDataScheme) {
aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
return false;
}
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
nsIChannel::LOAD_CLASSIFY_URI;
+ // No need to use CORS for sendBeacon unless it's a BLOB
+ nsSecurityFlags securityFlags = (!aData.IsNull() && aData.Value().IsBlob())
+ ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
+ : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
+ securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
doc,
- nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
- nsILoadInfo::SEC_COOKIES_INCLUDE,
+ securityFlags,
nsIContentPolicy::TYPE_BEACON,
nullptr, // aLoadGroup
nullptr, // aCallbacks
loadFlags);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2803,17 +2803,17 @@ nsDOMWindowUtils::WrapDOMFile(nsIFile *a
static bool
CheckLeafLayers(Layer* aLayer, const nsIntPoint& aOffset, nsIntRegion* aCoveredRegion)
{
gfx::Matrix transform;
if (!aLayer->GetTransform().Is2D(&transform) ||
transform.HasNonIntegerTranslation())
return false;
transform.NudgeToIntegers();
- nsIntPoint offset = aOffset + nsIntPoint(transform._31, transform._32);
+ IntPoint offset = aOffset + IntPoint::Truncate(transform._31, transform._32);
Layer* child = aLayer->GetFirstChild();
if (child) {
while (child) {
if (!CheckLeafLayers(child, offset, aCoveredRegion))
return false;
child = child->GetNextSibling();
}
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7719,27 +7719,27 @@ nsGlobalWindow::GetTopWindowRoot()
nsCOMPtr<nsPIWindowRoot> window = do_QueryInterface(piWin->GetChromeEventHandler());
return window.forget();
}
void
nsGlobalWindow::Scroll(double aXScroll, double aYScroll)
{
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
- CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
- mozilla::ToZeroIfNonfinite(aYScroll));
+ auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+ mozilla::ToZeroIfNonfinite(aYScroll));
ScrollTo(scrollPos, ScrollOptions());
}
void
nsGlobalWindow::ScrollTo(double aXScroll, double aYScroll)
{
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
- CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
- mozilla::ToZeroIfNonfinite(aYScroll));
+ auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+ mozilla::ToZeroIfNonfinite(aYScroll));
ScrollTo(scrollPos, ScrollOptions());
}
void
nsGlobalWindow::ScrollTo(const ScrollToOptions& aOptions)
{
FlushPendingNotifications(Flush_Layout);
nsIScrollableFrame *sf = GetScrollFrame();
@@ -7798,18 +7798,18 @@ nsGlobalWindow::ScrollTo(const CSSIntPoi
void
nsGlobalWindow::ScrollBy(double aXScrollDif, double aYScrollDif)
{
FlushPendingNotifications(Flush_Layout);
nsIScrollableFrame *sf = GetScrollFrame();
if (sf) {
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
- CSSIntPoint scrollDif(mozilla::ToZeroIfNonfinite(aXScrollDif),
- mozilla::ToZeroIfNonfinite(aYScrollDif));
+ auto scrollDif = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScrollDif),
+ mozilla::ToZeroIfNonfinite(aYScrollDif));
// It seems like it would make more sense for ScrollBy to use
// SMOOTH mode, but tests seem to depend on the synchronous behaviour.
// Perhaps Web content does too.
ScrollTo(sf->GetScrollPositionCSSPixels() + scrollDif, ScrollOptions());
}
}
void
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -128,56 +128,68 @@ nsLocation::CheckURL(nsIURI* aURI, nsIDo
NS_ENSURE_SUCCESS(rv, rv);
// Make the load's referrer reflect changes to the document's URI caused by
// push/replaceState, if possible. First, get the document corresponding to
// fp. If the document's original URI (i.e. its URI before
// push/replaceState) matches the principal's URI, use the document's
// current URI as the referrer. If they don't match, use the principal's
// URI.
+ //
+ // The triggering principal for this load should be the principal of the
+ // incumbent document (which matches where the referrer information is
+ // coming from) when there is an incumbent document, and the subject
+ // principal otherwise. Note that the URI in the triggering principal
+ // may not match the referrer URI in various cases, notably including
+ // the cases when the incumbent document's document URI was modified
+ // after the document was loaded.
- nsCOMPtr<nsIDocument> doc;
- nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
nsCOMPtr<nsPIDOMWindowInner> incumbent =
do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
- if (incumbent) {
- doc = incumbent->GetDoc();
- }
+ nsCOMPtr<nsIDocument> doc = incumbent ? incumbent->GetDoc() : nullptr;
+
if (doc) {
+ nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
docOriginalURI = doc->GetOriginalURI();
docCurrentURI = doc->GetDocumentURI();
rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
NS_ENSURE_SUCCESS(rv, rv);
+
+ owner = doc->NodePrincipal();
referrerPolicy = doc->GetReferrerPolicy();
- }
-
- bool urisEqual = false;
- if (docOriginalURI && docCurrentURI && principalURI) {
- principalURI->Equals(docOriginalURI, &urisEqual);
- }
- if (urisEqual) {
- sourceURI = docCurrentURI;
- }
- else {
- // Use principalURI as long as it is not an nsNullPrincipalURI.
- // We could add a method such as GetReferrerURI to principals to make this
- // cleaner, but given that we need to start using Source Browsing Context
- // for referrer (see Bug 960639) this may be wasted effort at this stage.
- if (principalURI) {
- bool isNullPrincipalScheme;
- rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
- &isNullPrincipalScheme);
- if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
- sourceURI = principalURI;
+ bool urisEqual = false;
+ if (docOriginalURI && docCurrentURI && principalURI) {
+ principalURI->Equals(docOriginalURI, &urisEqual);
+ }
+ if (urisEqual) {
+ sourceURI = docCurrentURI;
+ }
+ else {
+ // Use principalURI as long as it is not an nsNullPrincipalURI.
+ // We could add a method such as GetReferrerURI to principals to make this
+ // cleaner, but given that we need to start using Source Browsing Context
+ // for referrer (see Bug 960639) this may be wasted effort at this stage.
+ if (principalURI) {
+ bool isNullPrincipalScheme;
+ rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
+ &isNullPrincipalScheme);
+ if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
+ sourceURI = principalURI;
+ }
}
}
}
-
- owner = nsContentUtils::SubjectPrincipal();
+ else {
+ // No document; determine triggeringPrincipal by quering the
+ // subjectPrincipal, wich is the principal of the current JS
+ // compartment, or a null principal in case there is no
+ // compartment yet.
+ owner = nsContentUtils::SubjectPrincipal();
+ }
}
// Create load info
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
loadInfo->SetOwner(owner);
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4815,18 +4815,18 @@ CanvasRenderingContext2D::DrawDirectlyTo
}
gfxSize contextScale(contextMatrix.ScaleFactors(true));
// Scale the dest rect to include the context scale.
aDest.Scale(contextScale.width, contextScale.height);
// Scale the image size to the dest rect, and adjust the source rect to match.
gfxSize scale(aDest.width / aSrc.width, aDest.height / aSrc.height);
- nsIntSize scaledImageSize(std::ceil(aImgSize.width * scale.width),
- std::ceil(aImgSize.height * scale.height));
+ IntSize scaledImageSize = IntSize::Ceil(aImgSize.width * scale.width,
+ aImgSize.height * scale.height);
aSrc.Scale(scale.width, scale.height);
// We're wrapping tempTarget's (our) DrawTarget here, so we need to restore
// the matrix even though this is a temp gfxContext.
AutoRestoreTransform autoRestoreTransform(mTarget);
RefPtr<gfxContext> context = gfxContext::CreateOrNull(tempTarget);
if (!context) {
@@ -5040,17 +5040,17 @@ CanvasRenderingContext2D::DrawWindow(nsG
{
thebes = gfxContext::CreateOrNull(mTarget);
MOZ_ASSERT(thebes); // already checked the draw target above
// (in SupportsAzureContentForDrawTarget)
thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
matrix._22, matrix._31, matrix._32));
} else {
drawDT =
- gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize::Ceil(sw, sh),
SurfaceFormat::B8G8R8A8);
if (!drawDT || !drawDT->IsValid()) {
aError.Throw(NS_ERROR_FAILURE);
return;
}
thebes = gfxContext::CreateOrNull(drawDT);
MOZ_ASSERT(thebes); // alrady checked the draw target above
--- a/dom/media/raw/RawReader.cpp
+++ b/dom/media/raw/RawReader.cpp
@@ -55,20 +55,20 @@ nsresult RawReader::ReadMetadata(MediaIn
if (mMetadata.aspectDenominator == 0 ||
mMetadata.framerateDenominator == 0)
return NS_ERROR_FAILURE; // Invalid data
// Determine and verify frame display size.
float pixelAspectRatio = static_cast<float>(mMetadata.aspectNumerator) /
mMetadata.aspectDenominator;
- nsIntSize display(mMetadata.frameWidth, mMetadata.frameHeight);
+ nsIntSize display(uint32_t(mMetadata.frameWidth), uint32_t(mMetadata.frameHeight));
ScaleDisplayByAspectRatio(display, pixelAspectRatio);
mPicture = nsIntRect(0, 0, mMetadata.frameWidth, mMetadata.frameHeight);
- nsIntSize frameSize(mMetadata.frameWidth, mMetadata.frameHeight);
+ nsIntSize frameSize(uint32_t(mMetadata.frameWidth), uint32_t(mMetadata.frameHeight));
if (!IsValidVideoRegion(frameSize, mPicture, display)) {
// Video track's frame sizes will overflow. Fail.
return NS_ERROR_FAILURE;
}
mInfo.mVideo.mDisplay = display;
mFrameRate = static_cast<float>(mMetadata.framerateNumerator) /
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -176,17 +176,17 @@ AttachToContainerAsSurfaceTexture(ImageC
mozilla::gl::AndroidSurfaceTexture* surfTex = instance->AsSurfaceTexture();
if (!surfTex) {
return;
}
RefPtr<Image> img = new SurfaceTextureImage(
surfTex,
- gfx::IntSize(rect.width, rect.height),
+ gfx::IntSize::Truncate(rect.width, rect.height),
instance->OriginPos());
*out_image = img;
}
#endif
bool
nsPluginInstanceOwner::NeedsScrollImageLayer()
{
@@ -217,17 +217,17 @@ nsPluginInstanceOwner::GetImageContainer
return nullptr;
LayoutDeviceRect r = GetPluginRect();
// NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render
// into, set y-flip flags, etc, so we do this at the beginning.
float resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution)).Size();
- mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
+ mInstance->NotifySize(nsIntSize::Truncate(screenSize.width, screenSize.height));
container = LayerManager::CreateImageContainer();
// Try to get it as an EGLImage first.
RefPtr<Image> img;
AttachToContainerAsSurfaceTexture(container, mInstance, r, &img);
if (img) {
@@ -1579,17 +1579,17 @@ nsPluginInstanceOwner::GetVideos(nsTArra
already_AddRefed<ImageContainer>
nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo)
{
RefPtr<ImageContainer> container = LayerManager::CreateImageContainer();
RefPtr<Image> img = new SurfaceTextureImage(
aVideoInfo->mSurfaceTexture,
- gfx::IntSize(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height),
+ gfx::IntSize::Truncate(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height),
gl::OriginPos::BottomLeft);
container->SetCurrentImageInTransaction(img);
return container.forget();
}
void nsPluginInstanceOwner::Invalidate() {
NPRect rect;
--- a/dom/svg/SVGFETurbulenceElement.cpp
+++ b/dom/svg/SVGFETurbulenceElement.cpp
@@ -142,17 +142,17 @@ SVGFETurbulenceElement::GetPrimitiveDesc
// depending on the filter primitive region.
gfxRect firstPeriodInUserSpace(0, 0, 1 / fX, 1 / fY);
gfxRect firstPeriodInFilterSpace = aInstance->UserSpaceToFilterSpace(firstPeriodInUserSpace);
Size frequencyInFilterSpace(1 / firstPeriodInFilterSpace.width,
1 / firstPeriodInFilterSpace.height);
gfxPoint offset = firstPeriodInFilterSpace.TopLeft();
FilterPrimitiveDescription descr(PrimitiveType::Turbulence);
- descr.Attributes().Set(eTurbulenceOffset, IntPoint(offset.x, offset.y));
+ descr.Attributes().Set(eTurbulenceOffset, IntPoint::Truncate(offset.x, offset.y));
descr.Attributes().Set(eTurbulenceBaseFrequency, frequencyInFilterSpace);
descr.Attributes().Set(eTurbulenceSeed, seed);
descr.Attributes().Set(eTurbulenceNumOctaves, octaves);
descr.Attributes().Set(eTurbulenceStitchable, stitch == SVG_STITCHTYPE_STITCH);
descr.Attributes().Set(eTurbulenceType, type);
return descr;
}
--- a/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs
+++ b/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs
@@ -1,11 +1,11 @@
/*
* TestSever customized specifically for the needs of:
- * Bug 1080987 - navigator.sendBeacon() needs to sent origin header
+ * Bug 1280692 - navigator.sendBeacon() should not send origin header
*/
function handleRequest(request, response)
{
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/plain", false);
// case XHR-REQUEST: the xhr-request tries to query the
@@ -21,19 +21,26 @@ function handleRequest(request, response
// otherwise wait for the beacon request
response.processAsync();
setObjectState("xhr-response", response);
return;
}
// case BEACON-REQUEST: get the beacon header and
// store the header on the server.
- var header = request.getHeader("origin");
+ var header = "reset";
+ try {
+ header = request.getHeader("origin");
+ }
+ catch(e) {
+ header = "no-header";
+ }
setState("originHeader", header);
+
// if there is an xhr-request waiting, return the header now.
getObjectState("xhr-response", function(xhrResponse) {
if (!xhrResponse) {
return;
}
setState("originHeader", "");
xhrResponse.write(header);
xhrResponse.finish();
rename from dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs
rename to dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
--- a/dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs
+++ b/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
@@ -1,52 +1,47 @@
/*
* TestSever customized specifically for the needs of:
- * Bug 1111834 - sendBeacon() should not follow 30x redirect after preflight
+ * Bug 1280692 - sendBeacon() should follow 30x redirect
*
* Here is a sequence of the test:
- * [1] preflight channel (identified by the queryString 'beacon' and method 'OPTIONS')
- * [2] actual channel (identified by the queryString 'beacon') which gets redirected
- * [3] should never happen (the actual redirected request)
- * [4] xhr request (identified by the queryString 'verifyRedirectDidNotSucceed')
- * which checks if the state was not changed from 'green' to 'red'. If the channel
+ * [1] sendBeacon (identified by the queryString 'beacon') which gets redirected
+ * [2] redirected sendBeacon (identified by the queryString 'redirected') which
+ * updates the state idniciating that redirected sendBeacon succeeds.
+ * [3] xhr request (identified by the queryString 'verifyRedirectDidSucceed')
+ * which checks if the state was not changed from 'reset' to 'gree'. If the channel
* woulnd't be blocked correctly the redirected channel would set the state to 'red'.
*
*/
function handleRequest(request, response)
{
response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
- // [Sequence 4]
- if (request.queryString === "verifyRedirectDidNotSucceed") {
+ // [Sequence 3]
+ if (request.queryString === "verifyRedirectDidSucceed") {
var redirectState = getState("redirectState");
response.write(redirectState);
return;
}
- var originHeader = request.getHeader("origin");
- response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
- response.setHeader("Access-Control-Allow-Headers", "content-type", false);
- response.setHeader("Access-Control-Allow-Methods", "POST, GET", false);
- response.setHeader("Access-Control-Allow-Origin", originHeader, false);
- response.setHeader("Access-Control-Allow-Credentials", "true", false);
-
- // [Sequence 1,2]
+ // [Sequence 1]
if (request.queryString === "beacon") {
- setState("redirectState", "green");
- // [1]
- if (request.method == "OPTIONS") {
- response.setStatusLine(null, 200, "OK");
- return;
- }
- // [Sequence 2]
+ setState("redirectState", "reset");
var newLocation =
- "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs?redirected";
+ "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?redirected";
response.setStatusLine("1.1", 302, "Found");
response.setHeader("Location", newLocation, false);
return;
}
- // [Sequence 3]
+ // [Sequence 2]
+ if (request.queryString === "redirected") {
+ setState("redirectState", "green");
+ response.setStatusLine(null, 200, "OK");
+ return;
+ }
+
+ // we should never get here, but just in case let's
+ // set the state to something unexpected
setState("redirectState", "red");
response.setStatusLine(null, 200, "OK");
}
--- a/dom/tests/mochitest/beacon/mochitest.ini
+++ b/dom/tests/mochitest/beacon/mochitest.ini
@@ -1,16 +1,14 @@
[DEFAULT]
skip-if = buildapp == 'b2g'
support-files = beacon-frame.html
beacon-handler.sjs
beacon-preflight-handler.sjs
beacon-originheader-handler.sjs
- beacon-cors-redirect-handler.sjs
+ beacon-redirect-handler.sjs
[test_beacon.html]
[test_beaconFrame.html]
-[test_beaconPreflight.html]
-[test_beaconPreflightFailure.html]
[test_beaconPreflightWithCustomContentType.html]
[test_beaconContentPolicy.html]
[test_beaconOriginHeader.html]
-[test_beaconCORSRedirect.html]
+[test_beaconRedirect.html]
--- a/dom/tests/mochitest/beacon/test_beaconOriginHeader.html
+++ b/dom/tests/mochitest/beacon/test_beaconOriginHeader.html
@@ -1,49 +1,52 @@
<!DOCTYPE HTML>
<html>
<head>
- <title>Bug 1080987 - navigator.sendBeacon() needs to sent origin header</title>
+ <title>Bug 1280692 - navigator.sendBeacon() should not send origin header</title>
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="visibility: hidden">
<iframe style="width:100%;" id="testframe"></iframe>
</div>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs";
-const ORIGIN_HEADER = "http://mochi.test:8888";
+// no origin header should be sent with sendBeacon request;
+// server returns any origin-header or 'no-header' if there is no header sent.
+const ORIGIN_HEADER = "no-header";
/* Description of the test:
* We call sendBeacon() cross origin and make sure that the
- * origin header is actually set in the request.
+ * origin header is actually *not* set in the request.
*
- * Since sendBeacon() does not expect any response, we are storing the
+ * Since sendBeacon() does not expect any response, we are storing any
* header on the server (*.sjs) and use an XMLHttpRequest to actually
- * retrieve the header back from the server. We assert that the header
- * is indeed correct. Since sendBeacon() and also the XMLHttpRequest()
- * are performed in an asynchronous fashion, there is no guarantee that
- * the sendBeacon() is actually executed before the XMLHttpRequest().
+ * retrieve the potentially set header back from the server. We assert
+ * that the header is indeed *not* sent with the request. Since sendBeacon()
+ * and also the XMLHttpRequest() are performed in an asynchronous fashion,
+ * there is no guarantee that the sendBeacon() is actually executed before
+ * the XMLHttpRequest().
* Hence the xhr-response might be processed asynchronously.
*/
SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runTest);
function queryHeaderFromServer() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "beacon-originheader-handler.sjs?queryheader", true);
xhr.onload = function() {
- is(xhr.responseText, ORIGIN_HEADER, "SendBeacon sends right origin header");
+ is(xhr.responseText, ORIGIN_HEADER, "SendBeacon should not send origin header");
SimpleTest.finish();
};
xhr.onerror = function() {
ok(false, "xhr request returned error");
SimpleTest.finish();
};
xhr.send();
}
deleted file mode 100644
--- a/dom/tests/mochitest/beacon/test_beaconPreflight.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=936340
--->
-<head>
- <title>Test for Bug 936340</title>
- <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
- <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var beaconUrl = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs?beacon";
-
-var intervalID = null;
-
-function queryIfBeaconSucceeded() {
- clearInterval(intervalID);
- var xhr = new XMLHttpRequest();
- xhr.open("GET", "beacon-preflight-handler.sjs?verify", true);
- xhr.onload = function() {
- is(xhr.responseText, "green", "SendBeacon should have succeeded after preflight!");
- SimpleTest.finish();
- };
- xhr.onerror = function() {
- ok(false, "xhr request returned error");
- SimpleTest.finish();
- };
- xhr.send();
-}
-
-// not enabled by default yet.
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
-
-function beginTest() {
- var abv = new Uint8Array([0,1,2,3]);
- var sent = navigator.sendBeacon(beaconUrl, abv);
- ok(sent, "sending the beacon should start successfully");
-
- // we have to make sure sending the beacon did not fail, so
- // we have to wait for 2 seconds before we can query the result.
- intervalID = setInterval(queryIfBeaconSucceeded, 2000);
-}
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/tests/mochitest/beacon/test_beaconPreflightFailure.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1207556
--->
-<head>
- <title>Test for Bug 1207556</title>
- <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
- <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1207556">Mozilla Bug 1207556</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var beaconUrl = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs?fail";
-
-var intervalID = null;
-
-function queryIfBeaconSucceeded() {
- clearInterval(intervalID);
- var xhr = new XMLHttpRequest();
- xhr.open("GET", "beacon-preflight-handler.sjs?verify", true);
- xhr.onload = function() {
- is(xhr.responseText, "green", "SendBeacon should have failed because of a failed preflight!");
- SimpleTest.finish();
- };
- xhr.onerror = function() {
- ok(false, "xhr request returned error");
- SimpleTest.finish();
- };
- xhr.send();
-}
-
-// not enabled by default yet.
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
-
-function beginTest() {
- var abv = new Uint8Array([0,1,2,3]);
- var sent = navigator.sendBeacon(beaconUrl, abv);
- ok(sent, "sending the beacon should start successfully");
-
- // we have to make sure sending the beacon did not fail, so
- // we have to wait for 2 seconds before we can query the result.
- intervalID = setInterval(queryIfBeaconSucceeded, 2000);
-}
-
-</script>
-</pre>
-</body>
-</html>
rename from dom/tests/mochitest/beacon/test_beaconCORSRedirect.html
rename to dom/tests/mochitest/beacon/test_beaconRedirect.html
--- a/dom/tests/mochitest/beacon/test_beaconCORSRedirect.html
+++ b/dom/tests/mochitest/beacon/test_beaconRedirect.html
@@ -1,57 +1,57 @@
<!DOCTYPE HTML>
<html>
<head>
- <title>Bug 1111834 - sendBeacon() should not follow 30x redirect after preflight</title>
+ <title>Bug 1280692 - sendBeacon() should follow 30x redirect</title>
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="visibility: hidden">
<iframe style="width:100%;" id="testframe"></iframe>
</div>
<script class="testbody" type="text/javascript">
/* Description of the test:
- * We do perform a non simple sendBeacon request. After the preflight channel returns correctly
- * the actual channel is about to follow a 30x cross origin redirect, which is forbidden by the spec.
+ * We do perform a non simple sendBeacon request which should not use CORS and should follow
+ * a 30x cross origin redirect, which is allowed by the spec.
*/
SimpleTest.waitForExplicitFinish();
-const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs?beacon";
+const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?beacon";
SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runTest);
var intervalID = null;
function queryIfRedirectSucceeded() {
clearInterval(intervalID);
var xhr = new XMLHttpRequest();
- xhr.open("GET", "beacon-cors-redirect-handler.sjs?verifyRedirectDidNotSucceed", true);
+ xhr.open("GET", "beacon-redirect-handler.sjs?verifyRedirectDidSucceed", true);
xhr.onload = function() {
- is(xhr.responseText, "green", "SendBeacon does not follow cross origin redirects after preflight!");
+ is(xhr.responseText, "green", "SendBeacon should follow cross origin redirects!");
SimpleTest.finish();
};
xhr.onerror = function() {
ok(false, "xhr request returned error");
SimpleTest.finish();
};
xhr.send();
}
function runTest() {
var data = new Uint8Array([0,1,2,3]);
navigator.sendBeacon(BEACON_URL, data);
- // we have to make sure the channel did not follow the redirect hence
+ // we have to make sure the channel did follow the redirect hence
// we have to wait for 2 seconds before we can query the result.
intervalID = setInterval(queryIfRedirectSucceeded, 2000);
}
</script>
</pre>
</body>
</html>
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -334,17 +334,17 @@ DrawTargetD2D1::MaskSurface(const Patter
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
if (!bitmap) {
gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
return;
}
- IntSize size = IntSize(bitmap->GetSize().width, bitmap->GetSize().height);
+ IntSize size = IntSize::Truncate(bitmap->GetSize().width, bitmap->GetSize().height);
Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -533,17 +533,17 @@ DrawTargetSkia::DrawSurfaceWithShadow(So
// uses src-over to composite the resulting shadow.
// The canvas spec, however, states that the composite op must be used to
// composite the resulting shadow, so we must instead use a SkBlurImageFilter
// to blur the image ourselves.
SkPaint shadowPaint;
shadowPaint.setXfermodeMode(GfxOpToSkiaOp(aOperator));
- IntPoint shadowDest = RoundedToInt(aDest + aOffset);
+ auto shadowDest = IntPoint::Round(aDest + aOffset);
SkBitmap blurMask;
if (!UsingSkiaGPU() &&
bitmap.extractAlpha(&blurMask)) {
// Prefer using our own box blur instead of Skia's when we're
// not using the GPU. It currently performs much better than
// SkBlurImageFilter or SkBlurMaskFilter on the CPU.
AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
@@ -564,17 +564,17 @@ DrawTargetSkia::DrawSurfaceWithShadow(So
shadowPaint.setImageFilter(blurFilter);
shadowPaint.setColorFilter(colorFilter);
mCanvas->drawBitmap(bitmap, shadowDest.x, shadowDest.y, &shadowPaint);
}
// Composite the original image after the shadow
- IntPoint dest = RoundedToInt(aDest);
+ auto dest = IntPoint::Round(aDest);
mCanvas->drawBitmap(bitmap, dest.x, dest.y, &paint);
mCanvas->restore();
}
void
DrawTargetSkia::FillRect(const Rect &aRect,
const Pattern &aPattern,
--- a/gfx/2d/Point.h
+++ b/gfx/2d/Point.h
@@ -28,33 +28,80 @@ namespace gfx {
struct UnknownUnits {};
} // namespace gfx
template<> struct IsPixel<gfx::UnknownUnits> : TrueType {};
namespace gfx {
+/// Use this for parameters of functions to allow implicit conversions to
+/// integer types but not floating point types.
+/// We use this wrapper to prevent IntSize and IntPoint's constructors to
+/// take foating point values as parameters, and not require their constructors
+/// to have implementations for each permutation of integer types.
+template<typename T>
+struct IntParam {
+ constexpr MOZ_IMPLICIT IntParam(char val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned char val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(short val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned short val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(int val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned int val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(long long val) : value(val) {}
+ constexpr MOZ_IMPLICIT IntParam(unsigned long long val) : value(val) {}
+ template<typename Unit>
+ constexpr MOZ_IMPLICIT IntParam(IntCoordTyped<Unit> val) : value(val) {}
+
+ // Disable the evil ones!
+ MOZ_IMPLICIT IntParam(float val) = delete;
+ MOZ_IMPLICIT IntParam(double val) = delete;
+
+ T value;
+};
+
+template<class units, class> struct PointTyped;
+template<class units, class> struct SizeTyped;
+
template<class units>
struct IntPointTyped :
public BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> >,
public units {
static_assert(IsPixel<units>::value,
"'units' must be a coordinate system tag");
+ typedef IntParam<int32_t> ToInt;
typedef IntCoordTyped<units> Coord;
typedef BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> > Super;
constexpr IntPointTyped() : Super() {}
- constexpr IntPointTyped(int32_t aX, int32_t aY) : Super(Coord(aX), Coord(aY)) {}
- // The mixed-type constructors (int, Coord) and (Coord, int) are needed to
- // avoid ambiguities because Coord is implicitly convertible to int.
- constexpr IntPointTyped(int32_t aX, Coord aY) : Super(Coord(aX), aY) {}
- constexpr IntPointTyped(Coord aX, int32_t aY) : Super(aX, Coord(aY)) {}
- constexpr IntPointTyped(Coord aX, Coord aY) : Super(aX, aY) {}
+ constexpr IntPointTyped(ToInt aX, ToInt aY) : Super(Coord(aX.value), Coord(aY.value)) {}
+
+ static IntPointTyped<units> Round(float aX, float aY) {
+ return IntPointTyped(int32_t(floorf(aX + 0.5)), int32_t(floorf(aY + 0.5)));
+ }
+
+ static IntPointTyped<units> Ceil(float aX, float aY) {
+ return IntPointTyped(int32_t(ceil(aX)), int32_t(ceil(aY)));
+ }
+
+ static IntPointTyped<units> Floor(float aX, float aY) {
+ return IntPointTyped(int32_t(floorf(aX)), int32_t(floorf(aY)));
+ }
+
+ static IntPointTyped<units> Truncate(float aX, float aY) {
+ return IntPointTyped(int32_t(aX), int32_t(aY));
+ }
+
+ static IntPointTyped<units> Round(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Ceil(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Floor(const PointTyped<units, float>& aPoint);
+ static IntPointTyped<units> Truncate(const PointTyped<units, float>& aPoint);
// XXX When all of the code is ported, the following functions to convert to and from
// unknown types should be removed.
static IntPointTyped<units> FromUnknownPoint(const IntPointTyped<UnknownUnits>& aPoint) {
return IntPointTyped<units>(aPoint.x, aPoint.y);
}
@@ -94,24 +141,22 @@ struct PointTyped :
return PointTyped<UnknownUnits, F>(this->x, this->y);
}
};
typedef PointTyped<UnknownUnits> Point;
typedef PointTyped<UnknownUnits, double> PointDouble;
template<class units>
IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) {
- return IntPointTyped<units>(int32_t(floorf(aPoint.x + 0.5f)),
- int32_t(floorf(aPoint.y + 0.5f)));
+ return IntPointTyped<units>::Round(aPoint.x, aPoint.y);
}
-
+
template<class units>
IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) {
- return IntPointTyped<units>(int32_t(aPoint.x),
- int32_t(aPoint.y));
+ return IntPointTyped<units>::Truncate(aPoint.x, aPoint.y);
}
template<class units, class F = Float>
struct Point3DTyped :
public BasePoint3D< F, Point3DTyped<units, F> > {
static_assert(IsPixel<units>::value,
"'units' must be a coordinate system tag");
@@ -129,16 +174,44 @@ struct Point3DTyped :
Point3DTyped<UnknownUnits, F> ToUnknownPoint() const {
return Point3DTyped<UnknownUnits, F>(this->x, this->y, this->z);
}
};
typedef Point3DTyped<UnknownUnits> Point3D;
typedef Point3DTyped<UnknownUnits, double> PointDouble3D;
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Round(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Round(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Ceil(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Ceil(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Floor(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Floor(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Truncate(const PointTyped<units, float>& aPoint)
+{
+ return IntPointTyped::Truncate(aPoint.x, aPoint.y);
+}
+
template<class units, class F = Float>
struct Point4DTyped :
public BasePoint4D< F, Point4DTyped<units, F> > {
static_assert(IsPixel<units>::value,
"'units' must be a coordinate system tag");
typedef BasePoint4D< F, Point4DTyped<units, F> > Super;
@@ -165,20 +238,42 @@ typedef Point4DTyped<UnknownUnits, doubl
template<class units>
struct IntSizeTyped :
public BaseSize< int32_t, IntSizeTyped<units> >,
public units {
static_assert(IsPixel<units>::value,
"'units' must be a coordinate system tag");
+ typedef IntParam<int32_t> ToInt;
typedef BaseSize< int32_t, IntSizeTyped<units> > Super;
constexpr IntSizeTyped() : Super() {}
- constexpr IntSizeTyped(int32_t aWidth, int32_t aHeight) : Super(aWidth, aHeight) {}
+ constexpr IntSizeTyped(ToInt aWidth, ToInt aHeight) : Super(aWidth.value, aHeight.value) {}
+
+ static IntSizeTyped<units> Round(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(floorf(aWidth + 0.5)), int32_t(floorf(aHeight + 0.5)));
+ }
+
+ static IntSizeTyped<units> Truncate(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(aWidth), int32_t(aHeight));
+ }
+
+ static IntSizeTyped<units> Ceil(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(ceil(aWidth)), int32_t(ceil(aHeight)));
+ }
+
+ static IntSizeTyped<units> Floor(float aWidth, float aHeight) {
+ return IntSizeTyped(int32_t(floorf(aWidth)), int32_t(floorf(aHeight)));
+ }
+
+ static IntSizeTyped<units> Round(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Ceil(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Floor(const SizeTyped<units, float>& aSize);
+ static IntSizeTyped<units> Truncate(const SizeTyped<units, float>& aSize);
// XXX When all of the code is ported, the following functions to convert to and from
// unknown types should be removed.
static IntSizeTyped<units> FromUnknownSize(const IntSizeTyped<UnknownUnits>& aSize) {
return IntSizeTyped<units>(aSize.width, aSize.height);
}
@@ -217,12 +312,32 @@ typedef SizeTyped<UnknownUnits> Size;
typedef SizeTyped<UnknownUnits, double> SizeDouble;
template<class units>
IntSizeTyped<units> RoundedToInt(const SizeTyped<units>& aSize) {
return IntSizeTyped<units>(int32_t(floorf(aSize.width + 0.5f)),
int32_t(floorf(aSize.height + 0.5f)));
}
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Round(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Round(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Ceil(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Ceil(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Floor(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Floor(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Truncate(const SizeTyped<units, float>& aSize) {
+ return IntSizeTyped::Truncate(aSize.width, aSize.height);
+}
+
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_POINT_H_ */
--- a/gfx/gl/SharedSurfaceIO.cpp
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -17,17 +17,17 @@ namespace gl {
/*static*/ UniquePtr<SharedSurface_IOSurface>
SharedSurface_IOSurface::Create(const RefPtr<MacIOSurface>& ioSurf,
GLContext* gl,
bool hasAlpha)
{
MOZ_ASSERT(ioSurf);
MOZ_ASSERT(gl);
- gfx::IntSize size(ioSurf->GetWidth(), ioSurf->GetHeight());
+ auto size = gfx::IntSize::Truncate(ioSurf->GetWidth(), ioSurf->GetHeight());
typedef SharedSurface_IOSurface ptrT;
UniquePtr<ptrT> ret( new ptrT(ioSurf, gl, size, hasAlpha) );
return Move(ret);
}
void
SharedSurface_IOSurface::ProducerReleaseImpl()
@@ -209,18 +209,18 @@ SharedSurface_IOSurface::ReadbackByShare
////////////////////////////////////////////////////////////////////////
// SurfaceFactory_IOSurface
/*static*/ UniquePtr<SurfaceFactory_IOSurface>
SurfaceFactory_IOSurface::Create(GLContext* gl, const SurfaceCaps& caps,
const RefPtr<layers::ClientIPCAllocator>& allocator,
const layers::TextureFlags& flags)
{
- gfx::IntSize maxDims(MacIOSurface::GetMaxWidth(),
- MacIOSurface::GetMaxHeight());
+ auto maxDims = gfx::IntSize::Truncate(MacIOSurface::GetMaxWidth(),
+ MacIOSurface::GetMaxHeight());
typedef SurfaceFactory_IOSurface ptrT;
UniquePtr<ptrT> ret( new ptrT(gl, caps, allocator, flags, maxDims) );
return Move(ret);
}
UniquePtr<SharedSurface>
SurfaceFactory_IOSurface::CreateShared(const gfx::IntSize& size)
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -626,17 +626,17 @@ Layer::SnapTransformTranslation(const Ma
return aTransform;
}
Matrix matrix2D;
Matrix4x4 result;
if (aTransform.CanDraw2D(&matrix2D) &&
!matrix2D.HasNonTranslation() &&
matrix2D.HasNonIntegerTranslation()) {
- IntPoint snappedTranslation = RoundedToInt(matrix2D.GetTranslation());
+ auto snappedTranslation = IntPoint::Round(matrix2D.GetTranslation());
Matrix snappedMatrix = Matrix::Translation(snappedTranslation.x,
snappedTranslation.y);
result = Matrix4x4::From2D(snappedMatrix);
if (aResidualTransform) {
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
// (I.e., appying snappedMatrix after aResidualTransform gives the
// ideal transform.)
*aResidualTransform =
@@ -658,18 +658,17 @@ Layer::SnapTransformTranslation(const Ma
}
// Snap for 3D Transforms
Point3D transformedOrigin = aTransform * Point3D();
// Compute the transformed snap by rounding the values of
// transformed origin.
- IntPoint transformedSnapXY =
- RoundedToInt(Point(transformedOrigin.x, transformedOrigin.y));
+ auto transformedSnapXY = IntPoint::Round(transformedOrigin.x, transformedOrigin.y);
Matrix4x4 inverse = aTransform;
inverse.Invert();
// see Matrix4x4::ProjectPoint()
Float transformedSnapZ =
inverse._33 == 0 ? 0 : (-(transformedSnapXY.x * inverse._13 +
transformedSnapXY.y * inverse._23 +
inverse._43) / inverse._33);
Point3D transformedSnap =
@@ -717,19 +716,19 @@ Layer::SnapTransform(const Matrix4x4& aT
}
Matrix matrix2D;
Matrix4x4 result;
if (mManager->IsSnappingEffectiveTransforms() &&
aTransform.Is2D(&matrix2D) &&
gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
matrix2D.PreservesAxisAlignedRectangles()) {
- IntPoint transformedTopLeft = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopLeft()));
- IntPoint transformedTopRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopRight()));
- IntPoint transformedBottomRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.BottomRight()));
+ auto transformedTopLeft = IntPoint::Round(matrix2D * ToPoint(aSnapRect.TopLeft()));
+ auto transformedTopRight = IntPoint::Round(matrix2D * ToPoint(aSnapRect.TopRight()));
+ auto transformedBottomRight = IntPoint::Round(matrix2D * ToPoint(aSnapRect.BottomRight()));
Matrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect,
transformedTopLeft, transformedTopRight, transformedBottomRight);
result = Matrix4x4::From2D(snappedMatrix);
if (aResidualTransform && !snappedMatrix.IsSingular()) {
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
// (i.e., appying snappedMatrix after aResidualTransform gives the
@@ -1023,17 +1022,17 @@ Layer::TransformRectToRenderTarget(const
LayerRect rect(aRect);
RenderTargetRect quad = RenderTargetRect::FromUnknownRect(
GetEffectiveTransform().TransformBounds(rect.ToUnknownRect()));
return quad;
}
bool
Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
- nsIntPoint* aLayerOffset)
+ IntPoint* aLayerOffset)
{
MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
if (!GetParent()) {
return false;
}
IntPoint offset;
@@ -1041,17 +1040,17 @@ Layer::GetVisibleRegionRelativeToRootLay
for (Layer* layer = this; layer; layer = layer->GetParent()) {
gfx::Matrix matrix;
if (!layer->GetLocalTransform().Is2D(&matrix) ||
!matrix.IsTranslation()) {
return false;
}
// The offset of |layer| to its parent.
- IntPoint currentLayerOffset = RoundedToInt(matrix.GetTranslation());
+ auto currentLayerOffset = IntPoint::Round(matrix.GetTranslation());
// Translate the accumulated visible region of |this| by the offset of
// |layer|.
aResult.MoveBy(currentLayerOffset.x, currentLayerOffset.y);
// If the parent layer clips its lower layers, clip the visible region
// we're accumulating.
if (layer->GetLocalClipRect()) {
@@ -1068,17 +1067,17 @@ Layer::GetVisibleRegionRelativeToRootLay
gfx::Matrix siblingMatrix;
if (!sibling->GetLocalTransform().Is2D(&siblingMatrix) ||
!siblingMatrix.IsTranslation()) {
continue;
}
// Retreive the translation from sibling to |layer|. The accumulated
// visible region is currently oriented with |layer|.
- IntPoint siblingOffset = RoundedToInt(siblingMatrix.GetTranslation());
+ auto siblingOffset = IntPoint::Round(siblingMatrix.GetTranslation());
nsIntRegion siblingVisibleRegion(sibling->GetLocalVisibleRegion().ToUnknownRegion());
// Translate the siblings region to |layer|'s origin.
siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
// Apply the sibling's clip.
// Layer clip rects are not affected by the layer's transform.
Maybe<ParentLayerIntRect> clipRect = sibling->GetLocalClipRect();
if (clipRect) {
siblingVisibleRegion.AndWith(clipRect->ToUnknownRect());
@@ -1087,17 +1086,17 @@ Layer::GetVisibleRegionRelativeToRootLay
aResult.SubOut(siblingVisibleRegion);
}
// Keep track of the total offset for aLayerOffset. We use this in plugin
// positioning code.
offset += currentLayerOffset;
}
- *aLayerOffset = nsIntPoint(offset.x, offset.y);
+ *aLayerOffset = IntPoint(offset.x, offset.y);
return true;
}
Maybe<ParentLayerIntRect>
Layer::GetCombinedClipRect() const
{
Maybe<ParentLayerIntRect> clip = GetClipRect();
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1382,17 +1382,17 @@ public:
Maybe<ParentLayerIntRect> GetCombinedClipRect() const;
/**
* Retrieve the root level visible region for |this| taking into account
* clipping applied to parent layers of |this| as well as subtracting
* visible regions of higher siblings of this layer and each ancestor.
*
* Note translation values for offsets of visible regions and accumulated
- * aLayerOffset are integer rounded using Point's RoundedToInt.
+ * aLayerOffset are integer rounded using IntPoint::Round.
*
* @param aResult - the resulting visible region of this layer.
* @param aLayerOffset - this layer's total offset from the root layer.
* @return - false if during layer tree traversal a parent or sibling
* transform is found to be non-translational. This method returns early
* in this case, results will not be valid. Returns true on successful
* traversal.
*/
--- a/gfx/layers/MacIOSurfaceHelpers.cpp
+++ b/gfx/layers/MacIOSurfaceHelpers.cpp
@@ -31,17 +31,17 @@ CreateSourceSurfaceFromLockedMacIOSurfac
}
SurfaceFormat format =
(ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUV422)
? SurfaceFormat::B8G8R8X8
: SurfaceFormat::B8G8R8A8;
RefPtr<DataSourceSurface> dataSurface =
- Factory::CreateDataSourceSurface(IntSize(ioWidth, ioHeight), format);
+ Factory::CreateDataSourceSurface(IntSize::Truncate(ioWidth, ioHeight), format);
if (NS_WARN_IF(!dataSurface)) {
return nullptr;
}
DataSourceSurface::MappedSurface mappedSurface;
if (!dataSurface->Map(DataSourceSurface::WRITE, &mappedSurface)) {
return nullptr;
}
@@ -70,24 +70,24 @@ CreateSourceSurfaceFromLockedMacIOSurfac
rowSrc++;
}
}
/* Convert to RGB */
PlanarYCbCrData data;
data.mYChannel = (uint8_t*)aSurface->GetBaseAddressOfPlane(0);
data.mYStride = aSurface->GetBytesPerRow(0);
- data.mYSize = IntSize(ioWidth, ioHeight);
+ data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
data.mCbChannel = cbPlane.get();
data.mCrChannel = crPlane.get();
data.mCbCrStride = cbCrWidth;
- data.mCbCrSize = IntSize(cbCrWidth, cbCrHeight);
+ data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
data.mPicSize = data.mYSize;
- ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+ ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
} else if (ioFormat == SurfaceFormat::YUV422) {
/* Convert to YV16 */
size_t cbCrWidth = (ioWidth+1)>>1;
size_t cbCrHeight = ioHeight;
// Ensure our stride is a multiple of 32 to allow for memory aligned rows.
size_t cbCrStride = ALIGNED_32(cbCrWidth);
size_t strideDelta = cbCrStride - cbCrWidth;
MOZ_ASSERT(strideDelta <= 31);
@@ -123,24 +123,24 @@ CreateSourceSurfaceFromLockedMacIOSurfac
yDest += strideDelta << 1;
}
}
/* Convert to RGB */
PlanarYCbCrData data;
data.mYChannel = ALIGNEDPTR_32(yPlane.get());
data.mYStride = cbCrStride * 2;
- data.mYSize = IntSize(ioWidth, ioHeight);
+ data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
data.mCbChannel = ALIGNEDPTR_32(cbPlane.get());
data.mCrChannel = ALIGNEDPTR_32(crPlane.get());
data.mCbCrStride = cbCrStride;
- data.mCbCrSize = IntSize(cbCrWidth, cbCrHeight);
+ data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
data.mPicSize = data.mYSize;
- ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+ ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
} else {
unsigned char* ioData = (unsigned char*)aSurface->GetBaseAddress();
for (size_t i = 0; i < ioHeight; ++i) {
memcpy(mappedSurface.mData + i * mappedSurface.mStride,
ioData + i * bytesPerRow,
ioWidth * 4);
}
--- a/gfx/layers/MacIOSurfaceImage.h
+++ b/gfx/layers/MacIOSurfaceImage.h
@@ -20,17 +20,18 @@ public:
explicit MacIOSurfaceImage(MacIOSurface* aSurface)
: Image(nullptr, ImageFormat::MAC_IOSURFACE),
mSurface(aSurface)
{}
MacIOSurface* GetSurface() { return mSurface; }
gfx::IntSize GetSize() override {
- return gfx::IntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight());
+ return gfx::IntSize::Truncate(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
}
virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
virtual TextureClient* GetTextureClient(CompositableClient* aClient) override;
virtual MacIOSurfaceImage* AsMacIOSurfaceImage() override {
return this;
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -153,17 +153,17 @@ public:
return gfx::IntPoint(aPosition.x * scaledTileSize.width,
aPosition.y * scaledTileSize.height) + mTileOrigin;
}
const TilesPlacement& GetPlacement() const { return mTiles; }
const gfx::IntSize& GetTileSize() const { return mTileSize; }
- gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
+ gfx::IntSize GetScaledTileSize() const { return gfx::IntSize::Round(gfx::Size(mTileSize) / mResolution); }
unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
const nsIntRegion& GetValidRegion() const { return mValidRegion; }
const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -262,17 +262,17 @@ HitTestingTreeNode::HitTest(const Parent
return HitTestResult::HitNothing;
}
// convert into Layer coordinate space
Maybe<LayerPoint> pointInLayerPixels = Untransform(aPoint);
if (!pointInLayerPixels) {
return HitTestResult::HitNothing;
}
- LayerIntPoint point = RoundedToInt(pointInLayerPixels.ref());
+ auto point = LayerIntPoint::Round(pointInLayerPixels.ref());
// test against event regions in Layer coordinate space
if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
return HitTestResult::HitNothing;
}
if ((mOverride & EventRegionsOverride::ForceDispatchToContent) ||
mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
{
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -425,17 +425,17 @@ APZCCallbackHelper::ApplyCallbackTransfo
LayoutDeviceIntPoint
APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
const ScrollableLayerGuid& aGuid,
const CSSToLayoutDeviceScale& aScale)
{
LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
point = ApplyCallbackTransform(point / aScale, aGuid) * aScale;
- return gfx::RoundedToInt(point);
+ return LayoutDeviceIntPoint::Round(point);
}
void
APZCCallbackHelper::ApplyCallbackTransform(WidgetEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const CSSToLayoutDeviceScale& aScale)
{
if (aEvent.AsTouchEvent()) {
@@ -466,17 +466,17 @@ APZCCallbackHelper::DispatchSynthesizedM
Modifiers aModifiers,
nsIWidget* aWidget)
{
MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown ||
aMsg == eMouseUp || aMsg == eMouseLongTap);
WidgetMouseEvent event(true, aMsg, aWidget,
WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
- event.mRefPoint = LayoutDeviceIntPoint(aRefPoint.x, aRefPoint.y);
+ event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
event.mTime = aTime;
event.button = WidgetMouseEvent::eLeftButton;
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
event.mIgnoreRootScrollFrame = true;
if (aMsg != eMouseMove) {
event.mClickCount = 1;
}
event.mModifiers = aModifiers;
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -251,17 +251,17 @@ APZEventState::ProcessLongTap(const nsCO
mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled);
if (eventHandled) {
// Also send a touchcancel to content, so that listeners that might be
// waiting for a touchend don't trigger.
WidgetTouchEvent cancelTouchEvent(true, eTouchCancel, widget.get());
cancelTouchEvent.mModifiers = WidgetModifiersToDOMModifiers(aModifiers);
- LayoutDeviceIntPoint ldPoint = RoundedToInt(point * widget->GetDefaultScale());
+ auto ldPoint = LayoutDeviceIntPoint::Round(point * widget->GetDefaultScale());
cancelTouchEvent.mTouches.AppendElement(new mozilla::dom::Touch(mLastTouchIdentifier,
ldPoint, LayoutDeviceIntPoint(), 0, 0));
APZCCallbackHelper::DispatchWidgetEvent(cancelTouchEvent);
}
}
void
APZEventState::ProcessLongTapUp()
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -355,19 +355,19 @@ DrawSurfaceWithTextureCoords(DrawTarget
// Floating point error can accumulate above and we know our visible region
// is integer-aligned, so round it out.
sourceRect.Round();
// Compute a transform that maps sourceRect to aDestRect.
Matrix matrix =
gfxUtils::TransformRectToRect(sourceRect,
- gfx::IntPoint(aDestRect.x, aDestRect.y),
- gfx::IntPoint(aDestRect.XMost(), aDestRect.y),
- gfx::IntPoint(aDestRect.XMost(), aDestRect.YMost()));
+ gfx::IntPoint::Truncate(aDestRect.x, aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.YMost()));
// Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
gfx::Rect unitRect(0, 0, 1, 1);
ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT;
FillRectWithMask(aDest, aDestRect, aSource, aSamplingFilter, aOptions,
mode, aMask, aMaskTransform, &matrix);
}
@@ -621,17 +621,17 @@ BasicCompositor::DrawQuad(const gfx::Rec
dest->Flush();
RefPtr<SourceSurface> destSnapshot = dest->Snapshot();
SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
if (sourceMask) {
RefPtr<DrawTarget> transformDT =
- dest->CreateSimilarDrawTarget(IntSize(transformBounds.width, transformBounds.height),
+ dest->CreateSimilarDrawTarget(IntSize::Truncate(transformBounds.width, transformBounds.height),
SurfaceFormat::B8G8R8A8);
new3DTransform.PostTranslate(-transformBounds.x, -transformBounds.y, 0);
if (transformDT &&
transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
RefPtr<SourceSurface> transformSnapshot = transformDT->Snapshot();
// Transform the source by it's normal transform, and then the inverse
// of the mask transform so that it's in the mask's untransformed
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -931,17 +931,17 @@ BasicLayerManager::PaintLayer(gfxContext
effectiveTransform.TransformAndClipBounds(Rect(bounds),
ToRect(aTarget->GetClipExtents()));
xformBounds.RoundOut();
effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
effectiveTransform.PreTranslate(bounds.x, bounds.y, 0);
RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
RefPtr<DrawTarget> xformDT =
- untransformedDT->CreateSimilarDrawTarget(IntSize(xformBounds.width, xformBounds.height),
+ untransformedDT->CreateSimilarDrawTarget(IntSize::Truncate(xformBounds.width, xformBounds.height),
SurfaceFormat::B8G8R8A8);
RefPtr<SourceSurface> xformSurf;
if(xformDT && untransformedSurf &&
xformDT->Draw3DTransformedSurface(untransformedSurf, effectiveTransform)) {
xformSurf = xformDT->Snapshot();
}
if (xformSurf) {
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -844,17 +844,22 @@ ClientLayerManager::DependsOnStaleDevice
return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
}
already_AddRefed<PersistentBufferProvider>
ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat)
{
- if (gfxPrefs::PersistentBufferProviderSharedEnabled()) {
+ // Don't use a shared buffer provider if compositing is considered "not cheap"
+ // because the canvas will most likely be flattened into a thebes layer instead
+ // of being sent to the compositor, in which case rendering into shared memory
+ // is wasteful.
+ if (IsCompositingCheap() &&
+ gfxPrefs::PersistentBufferProviderSharedEnabled()) {
RefPtr<PersistentBufferProvider> provider
= PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
if (provider) {
return provider.forget();
}
}
return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -355,20 +355,21 @@ DeallocateTextureClient(TextureDeallocPa
actor->mOwnsTextureData = params.clientDeallocation;
actor->Destroy(actor->mCompositableForwarder);
// DestroyTextureData will be called by TextureChild::ActorDestroy
}
}
void TextureClient::Destroy(bool aForceSync)
{
- if (mActor) {
+ if (mActor && !mIsLocked) {
mActor->Lock();
}
+ mBorrowedDrawTarget = nullptr;
mReadLock = nullptr;
CancelWaitFenceHandleOnImageBridge();
RefPtr<TextureChild> actor = mActor;
mActor = nullptr;
if (actor && !actor->mDestroyed.compareExchange(false, true)) {
actor->Unlock();
@@ -514,17 +515,19 @@ TextureClient::Unlock()
mBorrowedDrawTarget = nullptr;
}
if (mOpenMode & OpenMode::OPEN_WRITE) {
mUpdated = true;
}
- mData->Unlock();
+ if (mData) {
+ mData->Unlock();
+ }
mIsLocked = false;
mOpenMode = OpenMode::OPEN_NONE;
UnlockActor();
}
void
TextureClient::EnableReadLock()
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -721,17 +721,17 @@ CreateTemporaryTargetAndCopyFromBackgrou
gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
visibleRect.width, visibleRect.height);
gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y);
gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform();
DebugOnly<gfx::Matrix> transform2d;
MOZ_ASSERT(transform.Is2D(&transform2d) && !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation());
- sourcePoint += gfx::IntPoint(transform._41, transform._42);
+ sourcePoint += gfx::IntPoint::Truncate(transform._41, transform._42);
sourcePoint -= compositor->GetCurrentRenderTarget()->GetOrigin();
return compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget, sourcePoint);
}
template<class ContainerT> void
RenderIntermediate(ContainerT* aContainer,
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -246,23 +246,23 @@ LayerManagerComposite::PostProcessLayers
return;
}
nsIntRegion localOpaque;
// Treat layers on the path to the root of the 3D rendering context as
// a giant layer if it is a leaf.
Matrix4x4 transform = GetAccTransformIn3DContext(aLayer);
Matrix transform2d;
- Maybe<nsIntPoint> integerTranslation;
+ Maybe<IntPoint> integerTranslation;
// If aLayer has a simple transform (only an integer translation) then we
// can easily convert aOpaqueRegion into pre-transform coordinates and include
// that region.
if (transform.Is2D(&transform2d)) {
if (transform2d.IsIntegerTranslation()) {
- integerTranslation = Some(TruncatedToInt(transform2d.GetTranslation()));
+ integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
localOpaque = aOpaqueRegion;
localOpaque.MoveBy(-*integerTranslation);
}
}
// Compute a clip that's the combination of our layer clip with the clip
// from our ancestors.
LayerComposite* composite = aLayer->AsLayerComposite();
--- a/gfx/thebes/PrintTargetWindows.cpp
+++ b/gfx/thebes/PrintTargetWindows.cpp
@@ -29,17 +29,17 @@ PrintTargetWindows::CreateOrNull(HDC aDC
// Figure out the cairo surface size - Windows we need to use the printable
// area of the page. Note: we only scale the printing using the LOGPIXELSY,
// so we use that when calculating the surface width as well as the height.
int32_t heightDPI = ::GetDeviceCaps(aDC, LOGPIXELSY);
float width =
(::GetDeviceCaps(aDC, HORZRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
float height =
(::GetDeviceCaps(aDC, VERTRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
- IntSize size(width, height);
+ IntSize size = IntSize::Truncate(width, height);
if (!Factory::CheckSurfaceSize(size)) {
return nullptr;
}
cairo_surface_t* surface = cairo_win32_printing_surface_create(aDC);
if (cairo_surface_status(surface)) {
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -515,35 +515,35 @@ ClippedImage::OptimalImageSizeForDest(co
}
if (needScale) {
// To avoid ugly sampling artifacts, ClippedImage needs the image size to
// be chosen such that the clipping region lies on pixel boundaries.
// First, we select a scale that's good for ClippedImage. An integer
// multiple of the size of the clipping region is always fine.
- nsIntSize scale(ceil(aDest.width / mClip.width),
- ceil(aDest.height / mClip.height));
+ IntSize scale = IntSize::Ceil(aDest.width / mClip.width,
+ aDest.height / mClip.height);
if (forceUniformScaling) {
scale.width = scale.height = max(scale.height, scale.width);
}
// Determine the size we'd prefer to render the inner image at, and ask the
// inner image what size we should actually use.
gfxSize desiredSize(imgWidth * scale.width, imgHeight * scale.height);
nsIntSize innerDesiredSize =
InnerImage()->OptimalImageSizeForDest(desiredSize, aWhichFrame,
aSamplingFilter, aFlags);
// To get our final result, we take the inner image's desired size and
// determine how large the clipped region would be at that scale. (Again, we
// ensure an integer multiple of the size of the clipping region.)
- nsIntSize finalScale(ceil(double(innerDesiredSize.width) / imgWidth),
- ceil(double(innerDesiredSize.height) / imgHeight));
+ IntSize finalScale = IntSize::Ceil(double(innerDesiredSize.width) / imgWidth,
+ double(innerDesiredSize.height) / imgHeight);
return mClip.Size() * finalScale;
}
MOZ_ASSERT(false,
"If ShouldClip() led us to draw then we should never get here");
return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
aSamplingFilter, aFlags);
}
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1818,17 +1818,17 @@ RasterImage::OptimalImageSizeForDest(con
MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
"Unexpected destination size");
if (mSize.IsEmpty() || aDest.IsEmpty()) {
return IntSize(0, 0);
}
- IntSize destSize(ceil(aDest.width), ceil(aDest.height));
+ IntSize destSize = IntSize::Ceil(aDest.width, aDest.height);
if (aSamplingFilter == SamplingFilter::GOOD &&
CanDownscaleDuringDecode(destSize, aFlags)) {
return destSize;
}
// We can't scale to this size. Use our intrinsic size for now.
return mSize;
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -1297,17 +1297,17 @@ VectorImage::OptimalImageSizeForDest(con
SamplingFilter aSamplingFilter,
uint32_t aFlags)
{
MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
"Unexpected destination size");
// We can rescale SVGs freely, so just return the provided destination size.
- return nsIntSize(ceil(aDest.width), ceil(aDest.height));
+ return nsIntSize::Ceil(aDest.width, aDest.height);
}
already_AddRefed<imgIContainer>
VectorImage::Unwrap()
{
nsCOMPtr<imgIContainer> self(this);
return self.forget();
}
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -106,17 +106,16 @@ class AsmJSGlobal
enum VarInitKind { InitConstant, InitImport };
enum ConstantKind { GlobalConstant, MathConstant };
private:
struct CacheablePod {
Which which_;
union {
struct {
- uint32_t globalDataOffset_;
VarInitKind initKind_;
union {
ValType importType_;
Val val_;
} u;
} var;
uint32_t ffiIndex_;
Scalar::Type viewType_;
@@ -145,20 +144,16 @@ class AsmJSGlobal
field_ = Move(field);
}
const char* field() const {
return field_.get();
}
Which which() const {
return pod.which_;
}
- uint32_t varGlobalDataOffset() const {
- MOZ_ASSERT(pod.which_ == Variable);
- return pod.u.var.globalDataOffset_;
- }
VarInitKind varInitKind() const {
MOZ_ASSERT(pod.which_ == Variable);
return pod.u.var.initKind_;
}
Val varInitVal() const {
MOZ_ASSERT(pod.which_ == Variable);
MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
return pod.u.var.u.val_;
@@ -1442,17 +1437,16 @@ class MOZ_STACK_CLASS ModuleValidator
return which_;
}
Type varOrConstType() const {
MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport);
return u.varOrConst.type_;
}
unsigned varOrConstIndex() const {
MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
- MOZ_ASSERT(u.varOrConst.index_ != -1u);
return u.varOrConst.index_;
}
bool isConst() const {
return which_ == ConstantLiteral || which_ == ConstantImport;
}
NumLit constLiteralValue() const {
MOZ_ASSERT(which_ == ConstantLiteral);
return u.varOrConst.literalValue_;
@@ -1846,61 +1840,59 @@ class MOZ_STACK_CLASS ModuleValidator
}
return true;
}
bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type, bool isConst) {
MOZ_ASSERT(type.isGlobalVarType());
MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
uint32_t index;
- if (!mg_.allocateGlobal(type.canonicalToValType(), isConst, &index))
+ if (!mg_.addGlobal(type.canonicalToValType(), isConst, &index))
return false;
Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
Global* global = validationLifo_.new_<Global>(which);
if (!global)
return false;
global->u.varOrConst.index_ = index;
global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : type).which();
if (isConst)
global->u.varOrConst.literalValue_ = lit;
if (!globalMap_.putNew(var, global))
return false;
AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
g.pod.u.var.u.val_ = lit.value();
- g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
return asmJSMetadata_->asmJSGlobals.append(Move(g));
}
bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type, bool isConst) {
MOZ_ASSERT(type.isGlobalVarType());
UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
if (!fieldChars)
return false;
uint32_t index;
ValType valType = type.canonicalToValType();
- if (!mg_.allocateGlobal(valType, isConst, &index))
+ if (!mg_.addGlobal(valType, isConst, &index))
return false;
Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
Global* global = validationLifo_.new_<Global>(which);
if (!global)
return false;
global->u.varOrConst.index_ = index;
global->u.varOrConst.type_ = type.which();
if (!globalMap_.putNew(var, global))
return false;
AsmJSGlobal g(AsmJSGlobal::Variable, Move(fieldChars));
g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
g.pod.u.var.u.importType_ = valType;
- g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
return asmJSMetadata_->asmJSGlobals.append(Move(g));
}
bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) {
UniqueChars fieldChars;
if (maybeField) {
fieldChars = StringToNewUTF8CharsZ(cx_, *maybeField);
if (!fieldChars)
return false;
@@ -3900,17 +3892,17 @@ CheckVarRef(FunctionValidator& f, ParseN
if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
switch (global->which()) {
case ModuleValidator::Global::ConstantLiteral:
*type = global->varOrConstType();
return f.writeConstExpr(global->constLiteralValue());
case ModuleValidator::Global::ConstantImport:
case ModuleValidator::Global::Variable: {
*type = global->varOrConstType();
- return f.encoder().writeExpr(Expr::LoadGlobal) &&
+ return f.encoder().writeExpr(Expr::GetGlobal) &&
f.encoder().writeVarU32(global->varOrConstIndex());
}
case ModuleValidator::Global::Function:
case ModuleValidator::Global::FFI:
case ModuleValidator::Global::MathBuiltinFunction:
case ModuleValidator::Global::AtomicsBuiltinFunction:
case ModuleValidator::Global::FuncPtrTable:
case ModuleValidator::Global::ArrayView:
@@ -4192,17 +4184,17 @@ CheckAssignName(FunctionValidator& f, Pa
Type rhsType;
if (!CheckExpr(f, rhs, &rhsType))
return false;
Type globType = global->varOrConstType();
if (!(rhsType <= globType))
return f.failf(lhs, "%s is not a subtype of %s", rhsType.toChars(), globType.toChars());
- if (!f.encoder().writeExpr(Expr::StoreGlobal))
+ if (!f.encoder().writeExpr(Expr::SetGlobal))
return false;
if (!f.encoder().writeVarU32(global->varOrConstIndex()))
return false;
*type = rhsType;
return true;
}
@@ -7812,48 +7804,30 @@ CheckBuffer(JSContext* cx, const AsmJSMe
if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers))
return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
}
return true;
}
static bool
-TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
- MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
-{
- HandleValue globalVal = args.get(0);
- HandleValue importVal = args.get(1);
- HandleValue bufferVal = args.get(2);
-
- RootedArrayBufferObjectMaybeShared buffer(cx);
- RootedWasmMemoryObject memory(cx);
- if (module.metadata().usesMemory()) {
- if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
- return false;
-
- memory = WasmMemoryObject::create(cx, buffer, nullptr);
- if (!memory)
- return false;
- }
-
- Vector<Val> valImports(cx);
+GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
+ HandleValue importVal, MutableHandle<FunctionVector> funcImports, ValVector* valImports)
+{
Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
if (!ffis.resize(metadata.numFFIs))
return false;
for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
switch (global.which()) {
case AsmJSGlobal::Variable: {
- // We don't have any global data into which to write the imported
- // values until after instantiation, so save them in a Vector.
Val val;
if (!ValidateGlobalVariable(cx, global, importVal, &val))
return false;
- if (!valImports.append(val))
+ if (!valImports->append(val))
return false;
break;
}
case AsmJSGlobal::FFI:
if (!ValidateFFI(cx, global, importVal, &ffis))
return false;
break;
case AsmJSGlobal::ArrayView:
@@ -7879,42 +7853,58 @@ TryInstantiate(JSContext* cx, CallArgs a
break;
case AsmJSGlobal::SimdOp:
if (!ValidateSimdOperation(cx, global, globalVal))
return false;
break;
}
}
+ for (const AsmJSImport& import : metadata.asmJSImports) {
+ if (!funcImports.append(ffis[import.ffiIndex()]))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
+ MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
+{
+ HandleValue globalVal = args.get(0);
+ HandleValue importVal = args.get(1);
+ HandleValue bufferVal = args.get(2);
+
+ RootedArrayBufferObjectMaybeShared buffer(cx);
+ RootedWasmMemoryObject memory(cx);
+ if (module.metadata().usesMemory()) {
+ if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
+ return false;
+
+ memory = WasmMemoryObject::create(cx, buffer, nullptr);
+ if (!memory)
+ return false;
+ }
+
+ ValVector valImports;
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
- for (const AsmJSImport& import : metadata.asmJSImports) {
- if (!funcs.append(ffis[import.ffiIndex()]))
- return false;
- }
+ if (!GetImports(cx, metadata, globalVal, importVal, &funcs, &valImports))
+ return false;
RootedWasmTableObject table(cx);
- if (!module.instantiate(cx, funcs, table, memory, nullptr, instanceObj))
+ if (!module.instantiate(cx, funcs, table, memory, valImports, nullptr, instanceObj))
return false;
RootedValue exportObjVal(cx);
if (!JS_GetProperty(cx, instanceObj, InstanceExportField, &exportObjVal))
return false;
MOZ_RELEASE_ASSERT(exportObjVal.isObject());
exportObj.set(&exportObjVal.toObject());
-
- // Now write the imported values into global data.
- uint8_t* globalData = instanceObj->instance().codeSegment().globalData();
- uint32_t valIndex = 0;
- for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
- if (global.which() == AsmJSGlobal::Variable)
- valImports[valIndex++].writePayload(globalData + global.varGlobalDataOffset());
- }
- MOZ_ASSERT(valIndex == valImports.length());
-
return true;
}
static MOZ_MUST_USE bool
MaybeAppendUTF8Name(JSContext* cx, const char* utf8Chars, MutableHandle<PropertyNameVector> names)
{
if (!utf8Chars)
return true;
--- a/js/src/asmjs/WasmAST.h
+++ b/js/src/asmjs/WasmAST.h
@@ -190,21 +190,23 @@ enum class AstExprKind
Block,
Branch,
BranchTable,
Call,
CallIndirect,
ComparisonOperator,
Const,
ConversionOperator,
+ GetGlobal,
GetLocal,
If,
Load,
Nop,
Return,
+ SetGlobal,
SetLocal,
Store,
TernaryOperator,
UnaryOperator,
Unreachable
};
class AstExpr : public AstNode
@@ -285,16 +287,51 @@ class AstSetLocal : public AstExpr
AstRef& local() {
return local_;
}
AstExpr& value() const {
return value_;
}
};
+class AstGetGlobal : public AstExpr
+{
+ AstRef global_;
+
+ public:
+ static const AstExprKind Kind = AstExprKind::GetGlobal;
+ explicit AstGetGlobal(AstRef global)
+ : AstExpr(Kind),
+ global_(global)
+ {}
+ AstRef& global() {
+ return global_;
+ }
+};
+
+class AstSetGlobal : public AstExpr
+{
+ AstRef global_;
+ AstExpr& value_;
+
+ public:
+ static const AstExprKind Kind = AstExprKind::SetGlobal;
+ AstSetGlobal(AstRef global, AstExpr& value)
+ : AstExpr(Kind),
+ global_(global),
+ value_(value)
+ {}
+ AstRef& global() {
+ return global_;
+ }
+ AstExpr& value() const {
+ return value_;
+ }
+};
+
class AstBlock : public AstExpr
{
Expr expr_;
AstName breakName_;
AstName continueName_;
AstExprVector exprs_;
public:
@@ -522,56 +559,102 @@ class AstResizable
AstResizable() : initial_(0), maximum_() {}
AstResizable(uint32_t initial, Maybe<uint32_t> maximum)
: initial_(initial), maximum_(maximum)
{}
uint32_t initial() const { return initial_; }
const Maybe<uint32_t>& maximum() const { return maximum_; }
};
+class AstGlobal : public AstNode
+{
+ AstName name_;
+ uint32_t flags_;
+ ValType type_;
+ Maybe<AstExpr*> init_;
+
+ public:
+ AstGlobal() : flags_(0), type_(ValType::Limit)
+ {}
+
+ explicit AstGlobal(AstName name, ValType type, uint32_t flags,
+ Maybe<AstExpr*> init = Maybe<AstExpr*>())
+ : name_(name), flags_(flags), type_(type), init_(init)
+ {}
+
+ AstName name() const { return name_; }
+ uint32_t flags() const { return flags_; }
+ ValType type() const { return type_; }
+
+ bool hasInit() const { return !!init_; }
+ AstExpr& init() const { MOZ_ASSERT(hasInit()); return **init_; }
+};
+
+typedef AstVector<AstGlobal*> AstGlobalVector;
+
class AstImport : public AstNode
{
AstName name_;
AstName module_;
AstName field_;
DefinitionKind kind_;
+
AstRef funcSig_;
AstResizable resizable_;
+ AstGlobal global_;
public:
AstImport(AstName name, AstName module, AstName field, AstRef funcSig)
: name_(name), module_(module), field_(field), kind_(DefinitionKind::Function), funcSig_(funcSig)
{}
AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, AstResizable resizable)
: name_(name), module_(module), field_(field), kind_(kind), resizable_(resizable)
{}
+ AstImport(AstName name, AstName module, AstName field, AstGlobal global)
+ : name_(name), module_(module), field_(field), kind_(DefinitionKind::Global), global_(global)
+ {}
+
AstName name() const { return name_; }
AstName module() const { return module_; }
AstName field() const { return field_; }
+
DefinitionKind kind() const { return kind_; }
- AstRef& funcSig() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return funcSig_; }
- AstResizable resizable() const { MOZ_ASSERT(kind_ != DefinitionKind::Function); return resizable_; }
+ AstRef& funcSig() {
+ MOZ_ASSERT(kind_ == DefinitionKind::Function);
+ return funcSig_;
+ }
+ AstResizable resizable() const {
+ MOZ_ASSERT(kind_ == DefinitionKind::Memory || kind_ == DefinitionKind::Table);
+ return resizable_;
+ }
+ const AstGlobal& global() const {
+ MOZ_ASSERT(kind_ == DefinitionKind::Global);
+ return global_;
+ }
};
class AstExport : public AstNode
{
AstName name_;
DefinitionKind kind_;
- AstRef func_;
+ AstRef ref_;
public:
- AstExport(AstName name, AstRef func)
- : name_(name), kind_(DefinitionKind::Function), func_(func)
+ AstExport(AstName name, DefinitionKind kind, AstRef ref)
+ : name_(name), kind_(kind), ref_(ref)
{}
explicit AstExport(AstName name, DefinitionKind kind)
: name_(name), kind_(kind)
{}
AstName name() const { return name_; }
DefinitionKind kind() const { return kind_; }
- AstRef& func() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return func_; }
+ AstRef& ref() {
+ MOZ_ASSERT(kind_ == DefinitionKind::Function || kind_ == DefinitionKind::Global);
+ return ref_;
+ }
};
class AstDataSegment : public AstNode
{
uint32_t offset_;
AstName text_;
public:
@@ -581,24 +664,25 @@ class AstDataSegment : public AstNode
uint32_t offset() const { return offset_; }
AstName text() const { return text_; }
};
typedef AstVector<AstDataSegment*> AstDataSegmentVector;
class AstElemSegment : public AstNode
{
- uint32_t offset_;
+ AstExpr* offset_;
AstRefVector elems_;
public:
- AstElemSegment(uint32_t offset, AstRefVector&& elems)
+ AstElemSegment(AstExpr* offset, AstRefVector&& elems)
: offset_(offset), elems_(Move(elems))
{}
- uint32_t offset() const { return offset_; }
+
+ AstExpr* offset() const { return offset_; }
AstRefVector& elems() { return elems_; }
const AstRefVector& elems() const { return elems_; }
};
typedef AstVector<AstElemSegment*> AstElemSegmentVector;
class AstStartFunc : public AstNode
{
@@ -631,27 +715,29 @@ class AstModule : public AstNode
ImportVector imports_;
Maybe<AstResizable> table_;
Maybe<AstResizable> memory_;
ExportVector exports_;
Maybe<AstStartFunc> startFunc_;
FuncVector funcs_;
AstDataSegmentVector dataSegments_;
AstElemSegmentVector elemSegments_;
+ AstGlobalVector globals_;
public:
explicit AstModule(LifoAlloc& lifo)
: lifo_(lifo),
sigs_(lifo),
sigMap_(lifo),
imports_(lifo),
exports_(lifo),
funcs_(lifo),
dataSegments_(lifo),
- elemSegments_(lifo)
+ elemSegments_(lifo),
+ globals_(lifo)
{}
bool init() {
return sigMap_.init();
}
bool setMemory(AstResizable memory) {
if (memory_)
return false;
memory_.emplace(memory);
@@ -722,28 +808,34 @@ class AstModule : public AstNode
return sigs_;
}
bool append(AstFunc* func) {
return funcs_.append(func);
}
const FuncVector& funcs() const {
return funcs_;
}
+ bool append(AstImport* imp) {
+ return imports_.append(imp);
+ }
const ImportVector& imports() const {
return imports_;
}
- bool append(AstImport* imp) {
- return imports_.append(imp);
- }
bool append(AstExport* exp) {
return exports_.append(exp);
}
const ExportVector& exports() const {
return exports_;
}
+ bool append(AstGlobal* glob) {
+ return globals_.append(glob);
+ }
+ const AstGlobalVector& globals() const {
+ return globals_;
+ }
};
class AstUnaryOperator final : public AstExpr
{
Expr expr_;
AstExpr* op_;
public:
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -5463,38 +5463,59 @@ BaseCompiler::emitGetGlobal()
if (!iter_.readGetGlobal(mg_.globals, &id))
return false;
if (deadCode_)
return true;
const GlobalDesc& global = mg_.globals[id];
- switch (global.type) {
+ if (global.isConstant()) {
+ Val value = global.constantValue();
+ switch (value.type()) {
+ case ValType::I32:
+ pushI32(value.i32());
+ break;
+ case ValType::I64:
+ pushI64(value.i64());
+ break;
+ case ValType::F32:
+ pushF32(value.f32());
+ break;
+ case ValType::F64:
+ pushF64(value.f64());
+ break;
+ default:
+ MOZ_CRASH("Global constant type");
+ }
+ return true;
+ }
+
+ switch (global.type()) {
case ValType::I32: {
RegI32 rv = needI32();
- loadGlobalVarI32(global.globalDataOffset, rv);
+ loadGlobalVarI32(global.offset(), rv);
pushI32(rv);
break;
}
case ValType::I64: {
RegI64 rv = needI64();
- loadGlobalVarI64(global.globalDataOffset, rv);
+ loadGlobalVarI64(global.offset(), rv);
pushI64(rv);
break;
}
case ValType::F32: {
RegF32 rv = needF32();
- loadGlobalVarF32(global.globalDataOffset, rv);
+ loadGlobalVarF32(global.offset(), rv);
pushF32(rv);
break;
}
case ValType::F64: {
RegF64 rv = needF64();
- loadGlobalVarF64(global.globalDataOffset, rv);
+ loadGlobalVarF64(global.offset(), rv);
pushF64(rv);
break;
}
default:
MOZ_CRASH("Global variable type");
break;
}
return true;
@@ -5508,38 +5529,38 @@ BaseCompiler::emitSetGlobal()
if (!iter_.readSetGlobal(mg_.globals, &id, &unused_value))
return false;
if (deadCode_)
return true;
const GlobalDesc& global = mg_.globals[id];
- switch (global.type) {
+ switch (global.type()) {
case ValType::I32: {
RegI32 rv = popI32();
- storeGlobalVarI32(global.globalDataOffset, rv);
+ storeGlobalVarI32(global.offset(), rv);
pushI32(rv);
break;
}
case ValType::I64: {
RegI64 rv = popI64();
- storeGlobalVarI64(global.globalDataOffset, rv);
+ storeGlobalVarI64(global.offset(), rv);
pushI64(rv);
break;
}
case ValType::F32: {
RegF32 rv = popF32();
- storeGlobalVarF32(global.globalDataOffset, rv);
+ storeGlobalVarF32(global.offset(), rv);
pushF32(rv);
break;
}
case ValType::F64: {
RegF64 rv = popF64();
- storeGlobalVarF64(global.globalDataOffset, rv);
+ storeGlobalVarF64(global.offset(), rv);
pushF64(rv);
break;
}
default:
MOZ_CRASH("Global variable type");
break;
}
return true;
@@ -6031,19 +6052,19 @@ BaseCompiler::emitBody()
case Expr::CallImport:
CHECK_NEXT(emitCallImport(exprOffset));
// Locals and globals
case Expr::GetLocal:
CHECK_NEXT(emitGetLocal());
case Expr::SetLocal:
CHECK_NEXT(emitSetLocal());
- case Expr::LoadGlobal:
+ case Expr::GetGlobal:
CHECK_NEXT(emitGetGlobal());
- case Expr::StoreGlobal:
+ case Expr::SetGlobal:
CHECK_NEXT(emitSetGlobal());
// Select
case Expr::Select:
CHECK_NEXT(emitSelect());
// I32
case Expr::I32Const: {
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -23,16 +23,17 @@
namespace js {
namespace wasm {
static const uint32_t MagicNumber = 0x6d736100; // "\0asm"
static const uint32_t EncodingVersion = 0x0b;
static const char TypeSectionId[] = "type";
+static const char GlobalSectionId[] = "global";
static const char ImportSectionId[] = "import";
static const char FunctionSectionId[] = "function";
static const char TableSectionId[] = "table";
static const char MemorySectionId[] = "memory";
static const char ExportSectionId[] = "export";
static const char StartSectionId[] = "start";
static const char CodeSectionId[] = "code";
static const char ElemSectionId[] = "elem";
@@ -65,26 +66,33 @@ enum class TypeConstructor
{
Function = 0x40
};
enum class DefinitionKind
{
Function = 0x00,
Table = 0x01,
- Memory = 0x02
+ Memory = 0x02,
+ Global = 0x03
};
enum class ResizableFlags
{
Default = 0x1,
HasMaximum = 0x2,
AllowedMask = 0x3
};
+enum class GlobalFlags
+{
+ IsMutable = 0x1,
+ AllowedMask = 0x1
+};
+
enum class Expr
{
// Control flow operators
Nop = 0x00,
Block = 0x01,
Loop = 0x02,
If = 0x03,
Else = 0x04,
@@ -266,23 +274,25 @@ enum class Expr
I32Rotr = 0xb6,
I32Rotl = 0xb7,
I64Rotr = 0xb8,
I64Rotl = 0xb9,
// i64.eqz.
I64Eqz = 0xba,
+ // Global access.
+ GetGlobal = 0xc0,
+ SetGlobal = 0xc1,
+
// ------------------------------------------------------------------------
// The rest of these operators are currently only emitted internally when
// compiling asm.js and are rejected by wasm validation.
// asm.js-specific operators
- LoadGlobal = 0xc0,
- StoreGlobal,
I32Min,
I32Max,
I32Neg,
I32BitNot,
I32Abs,
F32StoreF64,
F64StoreF32,
F64Mod,
--- a/js/src/asmjs/WasmBinaryIterator.cpp
+++ b/js/src/asmjs/WasmBinaryIterator.cpp
@@ -324,20 +324,20 @@ wasm::Classify(Expr expr)
case Expr::F32x4store:
case Expr::F32x4store1:
case Expr::F32x4store2:
case Expr::F32x4store3:
return ExprKind::Store;
case Expr::Select:
return ExprKind::Select;
case Expr::GetLocal:
- case Expr::LoadGlobal:
+ case Expr::GetGlobal:
return ExprKind::GetVar;
case Expr::SetLocal:
- case Expr::StoreGlobal:
+ case Expr::SetGlobal:
return ExprKind::SetVar;
case Expr::Call:
return ExprKind::Call;
case Expr::CallIndirect:
return ExprKind::CallIndirect;
case Expr::CallImport:
return ExprKind::CallImport;
case Expr::Return:
--- a/js/src/asmjs/WasmBinaryIterator.h
+++ b/js/src/asmjs/WasmBinaryIterator.h
@@ -1206,17 +1206,17 @@ ExprIter<Policy>::readGetGlobal(const Gl
uint32_t validateId;
if (!readVarU32(&validateId))
return false;
if (Validate && validateId >= globals.length())
return fail("get_global index out of range");
- if (!push(ToExprType(globals[validateId].type)))
+ if (!push(ToExprType(globals[validateId].type())))
return false;
if (Output)
*id = validateId;
return true;
}
@@ -1228,17 +1228,20 @@ ExprIter<Policy>::readSetGlobal(const Gl
uint32_t validateId;
if (!readVarU32(&validateId))
return false;
if (Validate && validateId >= globals.length())
return fail("set_global index out of range");
- if (!topWithType(ToExprType(globals[validateId].type), value))
+ if (Validate && !globals[validateId].isMutable())
+ return fail("can't write an immutable global");
+
+ if (!topWithType(ToExprType(globals[validateId].type()), value))
return false;
if (Output)
*id = validateId;
return true;
}
--- a/js/src/asmjs/WasmBinaryToAST.cpp
+++ b/js/src/asmjs/WasmBinaryToAST.cpp
@@ -1387,30 +1387,96 @@ AstDecodeMemorySection(AstDecodeContext&
if (!c.d.finishSection(sectionStart, sectionSize))
return AstDecodeFail(c, "memory section byte size mismatch");
c.module().setMemory(AstResizable(initialSizePages, Some(maxSizePages)));
return true;
}
static bool
+AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global)
+{
+ AstName name;
+ if (!AstDecodeGenerateName(c, AstName(u"global"), i, &name))
+ return false;
+
+ ValType type;
+ if (!c.d.readValType(&type))
+ return AstDecodeFail(c, "bad global type");
+
+ uint32_t flags;
+ if (!c.d.readVarU32(&flags))
+ return AstDecodeFail(c, "expected flags");
+
+ if (flags & ~uint32_t(GlobalFlags::AllowedMask))
+ return AstDecodeFail(c, "unexpected bits set in flags");
+
+ if (!AstDecodeExpr(c))
+ return false;
+
+ AstDecodeStackItem item = c.iter().getResult();
+ MOZ_ASSERT(item.popped == 0);
+ if (!item.expr)
+ return AstDecodeFail(c, "missing initializer expression");
+
+ AstExpr* init = item.expr;
+
+ if (!AstDecodeEnd(c))
+ return AstDecodeFail(c, "missing initializer end");
+
+ item = c.iter().getResult();
+ MOZ_ASSERT(item.terminationKind == AstDecodeTerminationKind::End);
+
+ *global = AstGlobal(name, type, flags, Some(init));
+ return true;
+}
+
+static bool
+AstDecodeGlobalSection(AstDecodeContext& c)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!c.d.startSection(GlobalSectionId, §ionStart, §ionSize))
+ return AstDecodeFail(c, "failed to start section");
+ if (sectionStart == Decoder::NotStarted)
+ return true;
+
+ uint32_t numGlobals;
+ if (!c.d.readVarU32(&numGlobals))
+ return AstDecodeFail(c, "expected number of globals");
+
+ for (uint32_t i = 0; i < numGlobals; i++) {
+ auto* global = new(c.lifo) AstGlobal;
+ if (!AstDecodeGlobal(c, i, global))
+ return false;
+ if (!c.module().append(global))
+ return false;
+ }
+
+ if (!c.d.finishSection(sectionStart, sectionSize))
+ return AstDecodeFail(c, "globals section byte size mismatch");
+
+ return true;
+}
+
+static bool
AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
{
uint32_t funcIndex;
if (!c.d.readVarU32(&funcIndex))
return AstDecodeFail(c, "expected export internal index");
if (funcIndex >= c.funcSigs().length())
return AstDecodeFail(c, "export function index out of range");
AstName fieldName;
if (!AstDecodeName(c, &fieldName))
return AstDecodeFail(c, "expected export name");
- *export_ = new(c.lifo) AstExport(fieldName, AstRef(AstName(), funcIndex));
+ *export_ = new(c.lifo) AstExport(fieldName, DefinitionKind::Function,
+ AstRef(AstName(), funcIndex));
if (!*export_)
return false;
return true;
}
static bool
AstDecodeExportSection(AstDecodeContext& c)
@@ -1642,16 +1708,19 @@ wasm::BinaryToAst(JSContext* cx, const u
return false;
if (!AstDecodeTableSection(c))
return false;
if (!AstDecodeMemorySection(c))
return false;
+ if (!AstDecodeGlobalSection(c))
+ return false;
+
if (!AstDecodeExportSection(c))
return false;
if (!AstDecodeCodeSection(c))
return false;
if (!AstDecodeDataSection(c))
return false;
--- a/js/src/asmjs/WasmBinaryToExperimentalText.cpp
+++ b/js/src/asmjs/WasmBinaryToExperimentalText.cpp
@@ -1560,19 +1560,19 @@ PrintExport(WasmPrintContext& c, AstExpo
if (!PrintIndent(c))
return false;
if (!c.buffer.append("export "))
return false;
if (export_.kind() == DefinitionKind::Memory) {
if (!c.buffer.append("memory"))
return false;
} else {
- const AstFunc* func = funcs[export_.func().index()];
+ const AstFunc* func = funcs[export_.ref().index()];
if (func->name().empty()) {
- if (!PrintInt32(c, export_.func().index()))
+ if (!PrintInt32(c, export_.ref().index()))
return false;
} else {
if (!PrintName(c, func->name()))
return false;
}
}
if (!c.buffer.append(" as \""))
return false;
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -1169,19 +1169,19 @@ RenderExport(WasmRenderContext& c, AstEx
if (!RenderEscapedString(c, export_.name()))
return false;
if (!c.buffer.append("\" "))
return false;
if (export_.kind() == DefinitionKind::Memory) {
if (!c.buffer.append("memory"))
return false;
} else {
- const AstFunc* func = funcs[export_.func().index()];
+ const AstFunc* func = funcs[export_.ref().index()];
if (func->name().empty()) {
- if (!RenderInt32(c, export_.func().index()))
+ if (!RenderInt32(c, export_.ref().index()))
return false;
} else {
if (!RenderName(c, func->name()))
return false;
}
}
if (!c.buffer.append(")\n"))
return false;
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -412,16 +412,17 @@ CacheableChars::sizeOfExcludingThis(Mall
size_t
Metadata::serializedSize() const
{
return sizeof(pod()) +
SerializedVectorSize(funcImports) +
SerializedVectorSize(funcExports) +
SerializedVectorSize(sigIds) +
+ SerializedPodVectorSize(globals) +
SerializedPodVectorSize(tables) +
SerializedPodVectorSize(memoryAccesses) +
SerializedPodVectorSize(boundsChecks) +
SerializedPodVectorSize(codeRanges) +
SerializedPodVectorSize(callSites) +
SerializedPodVectorSize(callThunks) +
SerializedPodVectorSize(funcNames) +
filename.serializedSize() +
@@ -430,16 +431,17 @@ Metadata::serializedSize() const
uint8_t*
Metadata::serialize(uint8_t* cursor) const
{
cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
cursor = SerializeVector(cursor, funcImports);
cursor = SerializeVector(cursor, funcExports);
cursor = SerializeVector(cursor, sigIds);
+ cursor = SerializePodVector(cursor, globals);
cursor = SerializePodVector(cursor, tables);
cursor = SerializePodVector(cursor, memoryAccesses);
cursor = SerializePodVector(cursor, boundsChecks);
cursor = SerializePodVector(cursor, codeRanges);
cursor = SerializePodVector(cursor, callSites);
cursor = SerializePodVector(cursor, callThunks);
cursor = SerializePodVector(cursor, funcNames);
cursor = filename.serialize(cursor);
@@ -449,16 +451,17 @@ Metadata::serialize(uint8_t* cursor) con
/* static */ const uint8_t*
Metadata::deserialize(const uint8_t* cursor)
{
(cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
(cursor = DeserializeVector(cursor, &funcImports)) &&
(cursor = DeserializeVector(cursor, &funcExports)) &&
(cursor = DeserializeVector(cursor, &sigIds)) &&
+ (cursor = DeserializePodVector(cursor, &globals)) &&
(cursor = DeserializePodVector(cursor, &tables)) &&
(cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
(cursor = DeserializePodVector(cursor, &boundsChecks)) &&
(cursor = DeserializePodVector(cursor, &codeRanges)) &&
(cursor = DeserializePodVector(cursor, &callSites)) &&
(cursor = DeserializePodVector(cursor, &callThunks)) &&
(cursor = DeserializePodVector(cursor, &funcNames)) &&
(cursor = filename.deserialize(cursor)) &&
@@ -467,16 +470,17 @@ Metadata::deserialize(const uint8_t* cur
}
size_t
Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
SizeOfVectorExcludingThis(funcExports, mallocSizeOf) +
SizeOfVectorExcludingThis(sigIds, mallocSizeOf) +
+ globals.sizeOfExcludingThis(mallocSizeOf) +
tables.sizeOfExcludingThis(mallocSizeOf) +
memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
boundsChecks.sizeOfExcludingThis(mallocSizeOf) +
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
callSites.sizeOfExcludingThis(mallocSizeOf) +
callThunks.sizeOfExcludingThis(mallocSizeOf) +
funcNames.sizeOfExcludingThis(mallocSizeOf) +
filename.sizeOfExcludingThis(mallocSizeOf) +
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -419,17 +419,16 @@ typedef Vector<char16_t, 64> TwoByteName
// Metadata holds all the data that is needed to describe compiled wasm code
// at runtime (as opposed to data that is only used to statically link or
// instantiate a module).
//
// Metadata is built incrementally by ModuleGenerator and then shared immutably
// between modules.
-
class MetadataCacheablePod
{
static const uint32_t NO_START_FUNCTION = UINT32_MAX;
static_assert(NO_START_FUNCTION > MaxFuncs, "sentinel value");
uint32_t startFuncIndex_;
public:
@@ -462,16 +461,17 @@ struct Metadata : ShareableBase<Metadata
virtual ~Metadata() {}
MetadataCacheablePod& pod() { return *this; }
const MetadataCacheablePod& pod() const { return *this; }
FuncImportVector funcImports;
FuncExportVector funcExports;
SigWithIdVector sigIds;
+ GlobalDescVector globals;
TableDescVector tables;
MemoryAccessVector memoryAccesses;
BoundsCheckVector boundsChecks;
CodeRangeVector codeRanges;
CallSiteVector callSites;
CallThunkVector callThunks;
NameInBytecodeVector funcNames;
CacheableChars filename;
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -231,16 +231,20 @@ DecodeExpr(FunctionDecoder& f)
case Expr::F32Const:
return f.iter().readF32Const(nullptr);
case Expr::F64Const:
return f.iter().readF64Const(nullptr);
case Expr::GetLocal:
return f.iter().readGetLocal(f.locals(), nullptr);
case Expr::SetLocal:
return f.iter().readSetLocal(f.locals(), nullptr, nullptr);
+ case Expr::GetGlobal:
+ return f.iter().readGetGlobal(f.mg().globals(), nullptr);
+ case Expr::SetGlobal:
+ return f.iter().readSetGlobal(f.mg().globals(), nullptr, nullptr);
case Expr::Select:
return f.iter().readSelect(nullptr, nullptr, nullptr, nullptr);
case Expr::Block:
return f.iter().readBlock();
case Expr::Loop:
return f.iter().readLoop();
case Expr::If:
return f.iter().readIf(nullptr);
@@ -742,16 +746,58 @@ DecodeResizableTable(Decoder& d, ModuleG
TableDesc table(TableKind::AnyFunction);
table.initial = resizable.initial;
table.maximum = resizable.maximum ? *resizable.maximum : UINT32_MAX;
return init->tables.append(table);
}
static bool
+DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
+{
+ if (!d.readValType(type))
+ return Fail(d, "bad global type");
+
+ if (*type == ValType::I64 && !IsI64Implemented())
+ return Fail(d, "int64 NYI");
+
+ uint32_t flags;
+ if (!d.readVarU32(&flags))
+ return Fail(d, "expected flags");
+
+ if (flags & ~uint32_t(GlobalFlags::AllowedMask))
+ return Fail(d, "unexpected bits set in flags");
+
+ *isMutable = flags & uint32_t(GlobalFlags::IsMutable);
+ return true;
+}
+
+static bool
+GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable)
+{
+ switch (type) {
+ case ValType::I32:
+ case ValType::F32:
+ case ValType::F64:
+ break;
+ case ValType::I64:
+ if (!JitOptions.wasmTestMode)
+ return Fail(d, "can't import/export an Int64 global to JS");
+ break;
+ default:
+ return Fail(d, "unexpected variable type in global import/export");
+ }
+
+ if (isMutable)
+ return Fail(d, "can't import/export mutable globals in the MVP");
+
+ return true;
+}
+
+static bool
DecodeImport(Decoder& d, bool newFormat, ModuleGeneratorData* init, ImportVector* imports)
{
if (!newFormat) {
const SigWithId* sig = nullptr;
if (!DecodeSignatureIndex(d, *init, &sig))
return false;
if (!CheckTypeForJS(d, *sig))
@@ -805,16 +851,27 @@ DecodeImport(Decoder& d, bool newFormat,
return false;
break;
}
case DefinitionKind::Memory: {
if (!DecodeResizableMemory(d, init))
return false;
break;
}
+ case DefinitionKind::Global: {
+ ValType type;
+ bool isMutable;
+ if (!DecodeGlobalType(d, &type, &isMutable))
+ return false;
+ if (!GlobalIsJSCompatible(d, type, isMutable))
+ return false;
+ if (!init->globals.append(GlobalDesc(type, isMutable, init->globals.length())))
+ return false;
+ break;
+ }
default:
return Fail(d, "unsupported import kind");
}
return imports->emplaceBack(Move(moduleName), Move(funcName), DefinitionKind(importKind));
}
static bool
@@ -941,16 +998,115 @@ DecodeMemorySection(Decoder& d, bool new
}
if (!d.finishSection(sectionStart, sectionSize))
return Fail(d, "memory section byte size mismatch");
return true;
}
+static bool
+DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
+ InitExpr* init)
+{
+ Expr expr;
+ if (!d.readExpr(&expr))
+ return Fail(d, "failed to read initializer type");
+
+ switch (expr) {
+ case Expr::I32Const: {
+ int32_t i32;
+ if (!d.readVarS32(&i32))
+ return Fail(d, "failed to read initializer i32 expression");
+ *init = InitExpr(Val(uint32_t(i32)));
+ break;
+ }
+ case Expr::I64Const: {
+ int64_t i64;
+ if (!d.readVarS64(&i64))
+ return Fail(d, "failed to read initializer i64 expression");
+ *init = InitExpr(Val(uint64_t(i64)));
+ break;
+ }
+ case Expr::F32Const: {
+ float f32;
+ if (!d.readFixedF32(&f32))
+ return Fail(d, "failed to read initializer f32 expression");
+ *init = InitExpr(Val(f32));
+ break;
+ }
+ case Expr::F64Const: {
+ double f64;
+ if (!d.readFixedF64(&f64))
+ return Fail(d, "failed to read initializer f64 expression");
+ *init = InitExpr(Val(f64));
+ break;
+ }
+ case Expr::GetGlobal: {
+ uint32_t i;
+ if (!d.readVarU32(&i))
+ return Fail(d, "failed to read get_global index in initializer expression");
+ if (i >= globals.length())
+ return Fail(d, "global index out of range in initializer expression");
+ if (!globals[i].isImport() || globals[i].isMutable())
+ return Fail(d, "initializer expression must reference a global immutable import");
+ *init = InitExpr(i, globals[i].type());
+ break;
+ }
+ default: {
+ return Fail(d, "unexpected initializer expression");
+ }
+ }
+
+ if (expected != init->type())
+ return Fail(d, "type mismatch: initializer type and expected type don't match");
+
+ Expr end;
+ if (!d.readExpr(&end) || end != Expr::End)
+ return Fail(d, "failed to read end of initializer expression");
+
+ return true;
+}
+
+static bool
+DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!d.startSection(GlobalSectionId, §ionStart, §ionSize))
+ return Fail(d, "failed to start section");
+ if (sectionStart == Decoder::NotStarted)
+ return true;
+
+ uint32_t numGlobals;
+ if (!d.readVarU32(&numGlobals))
+ return Fail(d, "expected number of globals");
+
+ if (numGlobals > MaxGlobals)
+ return Fail(d, "too many globals");
+
+ for (uint32_t i = 0; i < numGlobals; i++) {
+ ValType type;
+ bool isMutable;
+ if (!DecodeGlobalType(d, &type, &isMutable))
+ return false;
+
+ InitExpr initializer;
+ if (!DecodeInitializerExpression(d, init->globals, type, &initializer))
+ return false;
+
+ if (!init->globals.append(GlobalDesc(initializer, isMutable)))
+ return false;
+ }
+
+ if (!d.finishSection(sectionStart, sectionSize))
+ return Fail(d, "globals section byte size mismatch");
+
+ return true;
+}
+
typedef HashSet<const char*, CStringHasher, SystemAllocPolicy> CStringSet;
static UniqueChars
DecodeExportName(Decoder& d, CStringSet* dupSet)
{
UniqueChars exportName = MaybeDecodeName(d);
if (!exportName) {
Fail(d, "expected valid export name");
@@ -1027,16 +1183,30 @@ DecodeExport(Decoder& d, bool newFormat,
if (!d.readVarU32(&memoryIndex))
return Fail(d, "expected memory index");
if (memoryIndex > 0 || !mg.usesMemory())
return Fail(d, "exported memory index out of bounds");
return mg.addMemoryExport(Move(fieldName));
}
+ case DefinitionKind::Global: {
+ uint32_t globalIndex;
+ if (!d.readVarU32(&globalIndex))
+ return Fail(d, "expected global index");
+
+ if (globalIndex >= mg.globals().length())
+ return Fail(d, "exported global index out of bounds");
+
+ const GlobalDesc& global = mg.globals()[globalIndex];
+ if (!GlobalIsJSCompatible(d, global.type(), global.isMutable()))
+ return false;
+
+ return mg.addGlobalExport(Move(fieldName), globalIndex);
+ }
default:
return Fail(d, "unexpected export kind");
}
MOZ_CRASH("unreachable");
}
static bool
@@ -1128,17 +1298,16 @@ DecodeFunctionBody(Decoder& d, ModuleGen
memcpy(fg.bytes().begin(), bodyBegin, bodySize);
return mg.finishFuncDef(funcIndex, &fg);
}
static bool
DecodeStartSection(Decoder& d, ModuleGenerator& mg)
{
-
uint32_t sectionStart, sectionSize;
if (!d.startSection(StartSectionId, §ionStart, §ionSize))
return Fail(d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t startFuncIndex;
if (!d.readVarU32(&startFuncIndex))
@@ -1202,17 +1371,17 @@ static bool
DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGenerator& mg)
{
if (!newFormat) {
if (mg.tables().empty()) {
MOZ_ASSERT(oldElems.empty());
return true;
}
- return mg.addElemSegment(ElemSegment(0, 0, Move(oldElems)));
+ return mg.addElemSegment(ElemSegment(0, InitExpr(Val(uint32_t(0))), Move(oldElems)));
}
uint32_t sectionStart, sectionSize;
if (!d.startSection(ElemSectionId, §ionStart, §ionSize))
return Fail(d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
@@ -1226,48 +1395,45 @@ DecodeElemSection(Decoder& d, bool newFo
for (uint32_t i = 0, prevEnd = 0; i < numSegments; i++) {
ElemSegment seg;
if (!d.readVarU32(&seg.tableIndex))
return Fail(d, "expected table index");
if (seg.tableIndex >= mg.tables().length())
return Fail(d, "table index out of range");
- Expr expr;
- if (!d.readExpr(&expr))
- return Fail(d, "failed to read initializer expression");
+ if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &seg.offset))
+ return false;
- if (expr != Expr::I32Const)
- return Fail(d, "expected i32.const initializer expression");
-
- if (!d.readVarU32(&seg.offset))
- return Fail(d, "expected elem segment destination offset");
-
- if (seg.offset < prevEnd)
+ if (seg.offset.isVal() && seg.offset.val().i32() < prevEnd)
return Fail(d, "elem segments must be disjoint and ordered");
uint32_t numElems;
if (!d.readVarU32(&numElems))
return Fail(d, "expected segment size");
uint32_t tableLength = mg.tables()[seg.tableIndex].initial;
- if (seg.offset > tableLength || tableLength - seg.offset < numElems)
- return Fail(d, "element segment does not fit");
+ if (seg.offset.isVal()) {
+ uint32_t offset = seg.offset.val().i32();
+ if (offset > tableLength || tableLength - offset < numElems)
+ return Fail(d, "element segment does not fit");
+ }
if (!seg.elems.resize(numElems))
return false;
for (uint32_t i = 0; i < numElems; i++) {
if (!d.readVarU32(&seg.elems[i]))
return Fail(d, "failed to read element function index");
if (seg.elems[i] >= mg.numFuncSigs())
return Fail(d, "table element out of range");
}
- prevEnd = seg.offset + seg.elems.length();
+ if (seg.offset.isVal())
+ prevEnd = seg.offset.val().i32() + seg.elems.length();
if (!mg.addElemSegment(Move(seg)))
return false;
}
if (!d.finishSection(sectionStart, sectionSize))
return Fail(d, "data section byte size mismatch");
@@ -1454,16 +1620,19 @@ wasm::Compile(const ShareableBytes& byte
Uint32Vector oldElems;
if (!DecodeTableSection(d, newFormat, init.get(), &oldElems))
return nullptr;
bool memoryExported = false;
if (!DecodeMemorySection(d, newFormat, init.get(), &memoryExported))
return nullptr;
+ if (!DecodeGlobalSection(d, init.get()))
+ return nullptr;
+
ModuleGenerator mg(Move(imports));
if (!mg.init(Move(init), Move(args)))
return nullptr;
if (!DecodeExportSection(d, newFormat, memoryExported, mg))
return nullptr;
if (!DecodeStartSection(d, mg))
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -167,16 +167,23 @@ ModuleGenerator::init(UniqueModuleGenera
return false;
if (!metadata_->sigIds.emplaceBack(Move(copy), sig.id))
return false;
} else {
sig.id = SigIdDesc::immediate(sig);
}
}
+
+ for (GlobalDesc& global : shared_->globals) {
+ if (global.isConstant())
+ continue;
+ if (!allocateGlobal(&global))
+ return false;
+ }
} else {
MOZ_ASSERT(shared_->sigs.length() == MaxSigs);
MOZ_ASSERT(shared_->tables.length() == MaxTables);
MOZ_ASSERT(shared_->asmJSSigToTableIndex.length() == MaxSigs);
}
return true;
}
@@ -585,21 +592,21 @@ ModuleGenerator::allocateGlobalBytes(uin
if (!newGlobalDataLength.isValid())
return false;
linkData_.globalDataLength = newGlobalDataLength.value();
return true;
}
bool
-ModuleGenerator::allocateGlobal(ValType type, bool isConst, uint32_t* index)
+ModuleGenerator::allocateGlobal(GlobalDesc* global)
{
MOZ_ASSERT(!startedFuncDefs_);
unsigned width = 0;
- switch (type) {
+ switch (global->type()) {
case ValType::I32:
case ValType::F32:
width = 4;
break;
case ValType::I64:
case ValType::F64:
width = 8;
break;
@@ -616,18 +623,32 @@ ModuleGenerator::allocateGlobal(ValType
MOZ_CRASH("Limit");
break;
}
uint32_t offset;
if (!allocateGlobalBytes(width, width, &offset))
return false;
+ global->setOffset(offset);
+ return true;
+}
+
+bool
+ModuleGenerator::addGlobal(ValType type, bool isConst, uint32_t* index)
+{
+ MOZ_ASSERT(isAsmJS());
+ MOZ_ASSERT(!startedFuncDefs_);
+
*index = shared_->globals.length();
- return shared_->globals.append(GlobalDesc(type, offset, isConst));
+ GlobalDesc global(type, !isConst, *index);
+ if (!allocateGlobal(&global))
+ return false;
+
+ return shared_->globals.append(global);
}
void
ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
{
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(sigIndex == numSigs_);
numSigs_++;
@@ -708,17 +729,17 @@ ModuleGenerator::funcImport(uint32_t fun
{
MOZ_ASSERT(shared_->funcImports[funcImportIndex].sig);
return shared_->funcImports[funcImportIndex];
}
bool
ModuleGenerator::addFuncExport(UniqueChars fieldName, uint32_t funcIndex)
{
- return exports_.emplaceBack(Move(fieldName), funcIndex) &&
+ return exports_.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function) &&
exportedFuncs_.put(funcIndex);
}
bool
ModuleGenerator::addTableExport(UniqueChars fieldName)
{
MOZ_ASSERT(elemSegments_.empty());
externalTable_ = true;
@@ -727,16 +748,22 @@ ModuleGenerator::addTableExport(UniqueCh
bool
ModuleGenerator::addMemoryExport(UniqueChars fieldName)
{
return exports_.emplaceBack(Move(fieldName), DefinitionKind::Memory);
}
bool
+ModuleGenerator::addGlobalExport(UniqueChars fieldName, uint32_t globalIndex)
+{
+ return exports_.emplaceBack(Move(fieldName), globalIndex, DefinitionKind::Global);
+}
+
+bool
ModuleGenerator::setStartFunction(uint32_t funcIndex)
{
metadata_->initStartFuncIndex(funcIndex);
return exportedFuncs_.put(funcIndex);
}
bool
ModuleGenerator::startFuncDefs()
@@ -864,18 +891,16 @@ ModuleGenerator::finishFuncDefs()
linkData_.functionCodeLength = masm_.size();
finishedFuncDefs_ = true;
return true;
}
bool
ModuleGenerator::addElemSegment(ElemSegment&& seg)
{
- MOZ_ASSERT(seg.offset + seg.elems.length() <= shared_->tables[seg.tableIndex].initial);
-
if (externalTable_) {
for (uint32_t funcIndex : seg.elems) {
if (!exportedFuncs_.put(funcIndex))
return false;
}
}
return elemSegments_.append(Move(seg));
@@ -910,17 +935,17 @@ ModuleGenerator::initSigTableLength(uint
bool
ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices)
{
MOZ_ASSERT(isAsmJS());
uint32_t tableIndex = shared_->asmJSSigToTableIndex[sigIndex];
MOZ_ASSERT(shared_->tables[tableIndex].initial == elemFuncIndices.length());
- return elemSegments_.emplaceBack(tableIndex, 0, Move(elemFuncIndices));
+ return elemSegments_.emplaceBack(tableIndex, InitExpr(Val(uint32_t(0))), Move(elemFuncIndices));
}
SharedModule
ModuleGenerator::finish(const ShareableBytes& bytecode)
{
MOZ_ASSERT(!activeFunc_);
MOZ_ASSERT(finishedFuncDefs_);
@@ -965,16 +990,17 @@ ModuleGenerator::finish(const ShareableB
metadata_->memoryAccesses = masm_.extractMemoryAccesses();
metadata_->boundsChecks = masm_.extractBoundsChecks();
// Copy over data from the ModuleGeneratorData.
metadata_->memoryUsage = shared_->memoryUsage;
metadata_->minMemoryLength = shared_->minMemoryLength;
metadata_->maxMemoryLength = shared_->maxMemoryLength;
metadata_->tables = Move(shared_->tables);
+ metadata_->globals = Move(shared_->globals);
// These Vectors can get large and the excess capacity can be significant,
// so realloc them down to size.
metadata_->memoryAccesses.podResizeToFit();
metadata_->boundsChecks.podResizeToFit();
metadata_->codeRanges.podResizeToFit();
metadata_->callSites.podResizeToFit();
metadata_->callThunks.podResizeToFit();
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -131,16 +131,17 @@ class MOZ_STACK_CLASS ModuleGenerator
const CodeRange& funcCodeRange(uint32_t funcIndex) const;
MOZ_MUST_USE bool convertOutOfRangeBranchesToThunks();
MOZ_MUST_USE bool finishTask(IonCompileTask* task);
MOZ_MUST_USE bool finishFuncExports();
MOZ_MUST_USE bool finishCodegen();
MOZ_MUST_USE bool finishLinkData(Bytes& code);
MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
+ MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
public:
explicit ModuleGenerator(ImportVector&& imports);
~ModuleGenerator();
MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, CompileArgs&& args,
Metadata* maybeAsmJSMetadata = nullptr);
@@ -160,27 +161,27 @@ class MOZ_STACK_CLASS ModuleGenerator
uint32_t numSigs() const { return numSigs_; }
const SigWithId& sig(uint32_t sigIndex) const;
// Function declarations:
uint32_t numFuncSigs() const { return shared_->funcSigs.length(); }
const SigWithId& funcSig(uint32_t funcIndex) const;
// Globals:
- MOZ_MUST_USE bool allocateGlobal(ValType type, bool isConst, uint32_t* index);
- const GlobalDesc& global(unsigned index) const { return shared_->globals[index]; }
+ const GlobalDescVector& globals() const { return shared_->globals; }
// Imports:
uint32_t numFuncImports() const;
const FuncImportGenDesc& funcImport(uint32_t funcImportIndex) const;
// Exports:
MOZ_MUST_USE bool addFuncExport(UniqueChars fieldName, uint32_t funcIndex);
MOZ_MUST_USE bool addTableExport(UniqueChars fieldName);
MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName);
+ MOZ_MUST_USE bool addGlobalExport(UniqueChars fieldName, uint32_t globalIndex);
// Function definitions:
MOZ_MUST_USE bool startFuncDefs();
MOZ_MUST_USE bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg);
MOZ_MUST_USE bool finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg);
MOZ_MUST_USE bool finishFuncDefs();
// Start function:
@@ -196,16 +197,17 @@ class MOZ_STACK_CLASS ModuleGenerator
// asm.js lazy initialization:
void initSig(uint32_t sigIndex, Sig&& sig);
void initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
MOZ_MUST_USE bool initImport(uint32_t importIndex, uint32_t sigIndex);
MOZ_MUST_USE bool initSigTableLength(uint32_t sigIndex, uint32_t length);
MOZ_MUST_USE bool initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices);
void initMemoryUsage(MemoryUsage memoryUsage);
void bumpMinMemoryLength(uint32_t newMinMemoryLength);
+ MOZ_MUST_USE bool addGlobal(ValType type, bool isConst, uint32_t* index);
// Finish compilation, provided the list of imports and source bytecode.
// Both these Vectors may be empty (viz., b/c asm.js does different things
// for imports and source).
SharedModule finish(const ShareableBytes& bytecode);
};
// A FunctionGenerator encapsulates the generation of a single function body.
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -198,61 +198,16 @@ Instance::toggleProfiling(JSContext* cx)
MOZ_ASSERT(array[i] == from);
array[i] = to;
}
}
return true;
}
-static bool
-ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
-{
- if (!v.isObject()) {
- JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
- "i64 JS value must be an object");
- return false;
- }
-
- RootedObject obj(cx, &v.toObject());
-
- int32_t* i32 = (int32_t*)i64;
-
- RootedValue val(cx);
- if (!JS_GetProperty(cx, obj, "low", &val))
- return false;
- if (!ToInt32(cx, val, &i32[0]))
- return false;
-
- if (!JS_GetProperty(cx, obj, "high", &val))
- return false;
- if (!ToInt32(cx, val, &i32[1]))
- return false;
-
- return true;
-}
-
-static JSObject*
-CreateI64Object(JSContext* cx, int64_t i64)
-{
- RootedObject result(cx, JS_NewPlainObject(cx));
- if (!result)
- return nullptr;
-
- RootedValue val(cx, Int32Value(uint32_t(i64)));
- if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE))
- return nullptr;
-
- val = Int32Value(uint32_t(i64 >> 32));
- if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE))
- return nullptr;
-
- return result;
-}
-
bool
Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
MutableHandleValue rval)
{
const FuncImport& fi = metadata_->funcImports[funcImportIndex];
InvokeArgs args(cx);
if (!args.init(argc))
@@ -418,17 +373,18 @@ Instance::callImport_f64(int32_t funcImp
}
Instance::Instance(JSContext* cx,
UniqueCodeSegment codeSegment,
const Metadata& metadata,
const ShareableBytes* maybeBytecode,
HandleWasmMemoryObject memory,
SharedTableVector&& tables,
- Handle<FunctionVector> funcImports)
+ Handle<FunctionVector> funcImports,
+ const ValVector& globalImports)
: codeSegment_(Move(codeSegment)),
metadata_(&metadata),
maybeBytecode_(maybeBytecode),
memory_(memory),
tables_(Move(tables)),
profilingEnabled_(false)
{
MOZ_ASSERT(funcImports.length() == metadata.funcImports.length());
@@ -437,16 +393,50 @@ Instance::Instance(JSContext* cx,
for (size_t i = 0; i < metadata.funcImports.length(); i++) {
const FuncImport& fi = metadata.funcImports[i];
FuncImportExit& exit = funcImportToExit(fi);
exit.code = codeSegment_->code() + fi.interpExitCodeOffset();
exit.fun = funcImports[i];
exit.baselineScript = nullptr;
}
+ uint8_t* globalData = codeSegment_->globalData();
+
+ for (size_t i = 0; i < metadata.globals.length(); i++) {
+ const GlobalDesc& global = metadata.globals[i];
+ if (global.isConstant())
+ continue;
+
+ uint8_t* globalAddr = globalData + global.offset();
+ switch (global.kind()) {
+ case GlobalKind::Import: {
+ globalImports[global.importIndex()].writePayload(globalAddr);
+ break;
+ }
+ case GlobalKind::Variable: {
+ const InitExpr& init = global.initExpr();
+ switch (init.kind()) {
+ case InitExpr::Kind::Constant: {
+ init.val().writePayload(globalAddr);
+ break;
+ }
+ case InitExpr::Kind::GetGlobal: {
+ const GlobalDesc& imported = metadata.globals[init.globalIndex()];
+ globalImports[imported.importIndex()].writePayload(globalAddr);
+ break;
+ }
+ }
+ break;
+ }
+ case GlobalKind::Constant: {
+ MOZ_CRASH("skipped at the top");
+ }
+ }
+ }
+
if (memory)
*addressOfMemoryBase() = memory->buffer().dataPointerEither().unwrap();
for (size_t i = 0; i < tables_.length(); i++)
*addressOfTableBase(i) = tables_[i]->array();
updateStackLimit(cx);
}
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -84,17 +84,18 @@ class Instance
public:
Instance(JSContext* cx,
UniqueCodeSegment codeSegment,
const Metadata& metadata,
const ShareableBytes* maybeBytecode,
HandleWasmMemoryObject memory,
SharedTableVector&& tables,
- Handle<FunctionVector> funcImports);
+ Handle<FunctionVector> funcImports,
+ const ValVector& globalImports);
~Instance();
bool init(JSContext* cx);
void trace(JSTracer* trc);
const CodeSegment& codeSegment() const { return *codeSegment_; }
const Metadata& metadata() const { return *metadata_; }
const SharedTableVector& tables() const { return tables_; }
SharedMem<uint8_t*> memoryBase() const;
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -1843,31 +1843,71 @@ EmitSetLocal(FunctionCompiler& f)
static bool
EmitGetGlobal(FunctionCompiler& f)
{
uint32_t id;
if (!f.iter().readGetGlobal(f.mg().globals, &id))
return false;
const GlobalDesc& global = f.mg().globals[id];
- f.iter().setResult(f.loadGlobalVar(global.globalDataOffset, global.isConst,
- ToMIRType(global.type)));
+ if (!global.isConstant()) {
+ f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(),
+ ToMIRType(global.type())));
+ return true;
+ }
+
+ Val value = global.constantValue();
+ MIRType mirType = ToMIRType(value.type());
+
+ MDefinition* result;
+ switch (value.type()) {
+ case ValType::I32:
+ result = f.constant(Int32Value(value.i32()), mirType);
+ break;
+ case ValType::I64:
+ result = f.constant(value.i64());
+ break;
+ case ValType::F32:
+ result = f.constant(Float32Value(value.f32()), mirType);
+ break;
+ case ValType::F64:
+ result = f.constant(DoubleValue(value.f64()), mirType);
+ break;
+ case ValType::I8x16:
+ result = f.constant(SimdConstant::CreateX16(value.i8x16()), mirType);
+ break;
+ case ValType::I16x8:
+ result = f.constant(SimdConstant::CreateX8(value.i16x8()), mirType);
+ break;
+ case ValType::I32x4:
+ result = f.constant(SimdConstant::CreateX4(value.i32x4()), mirType);
+ break;
+ case ValType::F32x4:
+ result = f.constant(SimdConstant::CreateX4(value.f32x4()), mirType);
+ break;
+ default:
+ MOZ_CRASH("unexpected type in EmitGetGlobal");
+ }
+
+ f.iter().setResult(result);
return true;
}
static bool
EmitSetGlobal(FunctionCompiler& f)
{
uint32_t id;
MDefinition* value;
if (!f.iter().readSetGlobal(f.mg().globals, &id, &value))
return false;
const GlobalDesc& global = f.mg().globals[id];
- f.storeGlobalVar(global.globalDataOffset, value);
+ MOZ_ASSERT(global.isMutable());
+
+ f.storeGlobalVar(global.offset(), value);
return true;
}
template <typename MIRClass>
static bool
EmitUnary(FunctionCompiler& f, ValType operandType)
{
MDefinition* input;
@@ -2824,19 +2864,19 @@ EmitExpr(FunctionCompiler& f)
case Expr::CallImport:
return EmitCallImport(f, exprOffset);
// Locals and globals
case Expr::GetLocal:
return EmitGetLocal(f);
case Expr::SetLocal:
return EmitSetLocal(f);
- case Expr::LoadGlobal:
+ case Expr::GetGlobal:
return EmitGetGlobal(f);
- case Expr::StoreGlobal:
+ case Expr::SetGlobal:
return EmitSetGlobal(f);
// Select
case Expr::Select:
return EmitSelect(f);
// I32
case Expr::I32Const: {
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -17,21 +17,24 @@
*/
#include "asmjs/WasmJS.h"
#include "asmjs/WasmCompile.h"
#include "asmjs/WasmInstance.h"
#include "asmjs/WasmModule.h"
+#include "jit/JitOptions.h"
+
#include "jsobjinlines.h"
#include "vm/NativeObject-inl.h"
using namespace js;
+using namespace js::jit;
using namespace js::wasm;
bool
wasm::HasCompilerSupport(ExclusiveContext* cx)
{
if (!cx->jitSupportsFloatingPoint())
return false;
@@ -84,25 +87,31 @@ GetProperty(JSContext* cx, HandleObject
return false;
RootedId id(cx, AtomToId(atom));
return GetProperty(cx, obj, obj, id, v);
}
static bool
GetImports(JSContext* cx,
+ const Module& module,
HandleObject importObj,
- const ImportVector& imports,
MutableHandle<FunctionVector> funcImports,
MutableHandleWasmTableObject tableImport,
- MutableHandleWasmMemoryObject memoryImport)
+ MutableHandleWasmMemoryObject memoryImport,
+ ValVector* globalImports)
{
+ const ImportVector& imports = module.imports();
if (!imports.empty() && !importObj)
return Throw(cx, "no import object given");
+ const Metadata& metadata = module.metadata();
+
+ uint32_t globalIndex = 0;
+ const GlobalDescVector& globals = metadata.globals;
for (const Import& import : imports) {
RootedValue v(cx);
if (!GetProperty(cx, importObj, import.module.get(), &v))
return false;
if (strlen(import.func.get()) > 0) {
if (!v.isObject())
return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "an Object");
@@ -130,19 +139,63 @@ GetImports(JSContext* cx,
break;
case DefinitionKind::Memory:
if (!v.isObject() || !v.toObject().is<WasmMemoryObject>())
return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "a Memory");
MOZ_ASSERT(!memoryImport);
memoryImport.set(&v.toObject().as<WasmMemoryObject>());
break;
+
+ case DefinitionKind::Global:
+ Val val;
+ const GlobalDesc& global = globals[globalIndex++];
+ MOZ_ASSERT(global.importIndex() == globalIndex - 1);
+ MOZ_ASSERT(!global.isMutable());
+ switch (global.type()) {
+ case ValType::I32: {
+ int32_t i32;
+ if (!ToInt32(cx, v, &i32))
+ return false;
+ val = Val(uint32_t(i32));
+ break;
+ }
+ case ValType::I64: {
+ MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in JS");
+ int64_t i64;
+ if (!ReadI64Object(cx, v, &i64))
+ return false;
+ val = Val(uint64_t(i64));
+ break;
+ }
+ case ValType::F32: {
+ double d;
+ if (!ToNumber(cx, v, &d))
+ return false;
+ val = Val(float(d));
+ break;
+ }
+ case ValType::F64: {
+ double d;
+ if (!ToNumber(cx, v, &d))
+ return false;
+ val = Val(d);
+ break;
+ }
+ default: {
+ MOZ_CRASH("unexpected import value type");
+ }
+ }
+ if (!globalImports->append(val))
+ return false;
}
}
+ MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
+
return true;
}
bool
wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
MutableHandleWasmInstanceObject instanceObj)
{
if (!CheckCompilerSupport(cx))
@@ -179,20 +232,21 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
else
ReportOutOfMemory(cx);
return false;
}
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
RootedWasmTableObject table(cx);
RootedWasmMemoryObject memory(cx);
- if (!GetImports(cx, importObj, module->imports(), &funcs, &table, &memory))
+ ValVector globals;
+ if (!GetImports(cx, *module, importObj, &funcs, &table, &memory, &globals))
return false;
- return module->instantiate(cx, funcs, table, memory, nullptr, instanceObj);
+ return module->instantiate(cx, funcs, table, memory, globals, nullptr, instanceObj);
}
static bool
InstantiateModule(JSContext* cx, unsigned argc, Value* vp)
{
MOZ_ASSERT(cx->options().wasm());
CallArgs args = CallArgsFromVp(argc, vp);
@@ -506,22 +560,23 @@ WasmInstanceObject::construct(JSContext*
return false;
}
importObj = &args[1].toObject();
}
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
RootedWasmTableObject table(cx);
RootedWasmMemoryObject memory(cx);
- if (!GetImports(cx, importObj, module.imports(), &funcs, &table, &memory))
+ ValVector globals;
+ if (!GetImports(cx, module, importObj, &funcs, &table, &memory, &globals))
return false;
RootedObject instanceProto(cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
RootedWasmInstanceObject instanceObj(cx);
- if (!module.instantiate(cx, funcs, table, memory, instanceProto, &instanceObj))
+ if (!module.instantiate(cx, funcs, table, memory, globals, instanceProto, &instanceObj))
return false;
args.rval().setObject(*instanceObj);
return true;
}
Instance&
WasmInstanceObject::instance() const
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -16,25 +16,72 @@
* limitations under the License.
*/
#include "asmjs/WasmModule.h"
#include "asmjs/WasmInstance.h"
#include "asmjs/WasmJS.h"
#include "asmjs/WasmSerialize.h"
+#include "jit/JitOptions.h"
#include "vm/ArrayBufferObject-inl.h"
#include "vm/Debugger-inl.h"
using namespace js;
+using namespace js::jit;
using namespace js::wasm;
const char wasm::InstanceExportField[] = "exports";
+JSObject*
+js::wasm::CreateI64Object(JSContext* cx, int64_t i64)
+{
+ RootedObject result(cx, JS_NewPlainObject(cx));
+ if (!result)
+ return nullptr;
+
+ RootedValue val(cx, Int32Value(uint32_t(i64)));
+ if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE))
+ return nullptr;
+
+ val = Int32Value(uint32_t(i64 >> 32));
+ if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE))
+ return nullptr;
+
+ return result;
+}
+
+bool
+js::wasm::ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
+{
+ if (!v.isObject()) {
+ JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
+ "i64 JS value must be an object");
+ return false;
+ }
+
+ RootedObject obj(cx, &v.toObject());
+
+ int32_t* i32 = (int32_t*)i64;
+
+ RootedValue val(cx);
+ if (!JS_GetProperty(cx, obj, "low", &val))
+ return false;
+ if (!ToInt32(cx, val, &i32[0]))
+ return false;
+
+ if (!JS_GetProperty(cx, obj, "high", &val))
+ return false;
+ if (!ToInt32(cx, val, &i32[1]))
+ return false;
+
+ return true;
+}
+
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
// On MIPS, CodeLabels are instruction immediates so InternalLinks only
// patch instruction immediates.
LinkData::InternalLink::InternalLink(Kind kind)
{
MOZ_ASSERT(kind == CodeLabel || kind == InstructionImmediate);
}
@@ -153,35 +200,42 @@ Import::deserialize(const uint8_t* curso
size_t
Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return module.sizeOfExcludingThis(mallocSizeOf) +
func.sizeOfExcludingThis(mallocSizeOf);
}
-Export::Export(UniqueChars fieldName, uint32_t funcIndex)
+Export::Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind)
: fieldName_(Move(fieldName))
{
- pod.kind_ = DefinitionKind::Function;
- pod.funcIndex_ = funcIndex;
+ pod.kind_ = kind;
+ pod.index_ = index;
}
Export::Export(UniqueChars fieldName, DefinitionKind kind)
: fieldName_(Move(fieldName))
{
pod.kind_ = kind;
- pod.funcIndex_ = 0;
+ pod.index_ = 0;
}
uint32_t
Export::funcIndex() const
{
MOZ_ASSERT(pod.kind_ == DefinitionKind::Function);
- return pod.funcIndex_;
+ return pod.index_;
+}
+
+uint32_t
+Export::globalIndex() const
+{
+ MOZ_ASSERT(pod.kind_ == DefinitionKind::Global);
+ return pod.index_;
}
size_t
Export::serializedSize() const
{
return fieldName_.serializedSize() +
sizeof(pod);
}
@@ -207,31 +261,34 @@ Export::sizeOfExcludingThis(MallocSizeOf
{
return fieldName_.sizeOfExcludingThis(mallocSizeOf);
}
size_t
ElemSegment::serializedSize() const
{
return sizeof(tableIndex) +
+ sizeof(offset) +
SerializedPodVectorSize(elems);
}
uint8_t*
ElemSegment::serialize(uint8_t* cursor) const
{
cursor = WriteBytes(cursor, &tableIndex, sizeof(tableIndex));
+ cursor = WriteBytes(cursor, &offset, sizeof(offset));
cursor = SerializePodVector(cursor, elems);
return cursor;
}
const uint8_t*
ElemSegment::deserialize(const uint8_t* cursor)
{
(cursor = ReadBytes(cursor, &tableIndex, sizeof(tableIndex))) &&
+ (cursor = ReadBytes(cursor, &offset, sizeof(offset))) &&
(cursor = DeserializePodVector(cursor, &elems));
return cursor;
}
size_t
ElemSegment::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return elems.sizeOfExcludingThis(mallocSizeOf);
@@ -346,17 +403,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
dataSegments_.sizeOfExcludingThis(mallocSizeOf) +
SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
}
bool
Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
- HandleWasmTableObject tableObj) const
+ const ValVector& globalImports, HandleWasmTableObject tableObj) const
{
Instance& instance = instanceObj->instance();
const CodeSegment& codeSegment = instance.codeSegment();
const SharedTableVector& tables = instance.tables();
// Initialize tables that have a WasmTableObject first, so that this
// initialization can be done atomically.
if (tableObj && !tableObj->initialized() && !tableObj->init(cx, instanceObj))
@@ -364,30 +421,63 @@ Module::initElems(JSContext* cx, HandleW
// Initialize all remaining Tables that do not have objects.
for (const SharedTable& table : tables) {
if (!table->initialized())
table->init(codeSegment);
}
// Now that all tables have been initialized, write elements.
+ Vector<uint32_t> prevEnds(cx);
+ if (!prevEnds.appendN(0, tables.length()))
+ return false;
+
for (const ElemSegment& seg : elemSegments_) {
Table& table = *tables[seg.tableIndex];
- MOZ_ASSERT(seg.offset + seg.elems.length() <= table.length());
+
+ uint32_t offset;
+ switch (seg.offset.kind()) {
+ case InitExpr::Kind::Constant: {
+ offset = seg.offset.val().i32();
+ break;
+ }
+ case InitExpr::Kind::GetGlobal: {
+ const GlobalDesc& global = metadata_->globals[seg.offset.globalIndex()];
+ offset = globalImports[global.importIndex()].i32();
+ break;
+ }
+ }
+
+ uint32_t& prevEnd = prevEnds[seg.tableIndex];
+
+ if (offset < prevEnd) {
+ JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
+ "elem segments must be disjoint and ordered");
+ return false;
+ }
+
+ uint32_t tableLength = instance.metadata().tables[seg.tableIndex].initial;
+ if (offset > tableLength || tableLength - offset < seg.elems.length()) {
+ JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
+ "element segment does not fit");
+ return false;
+ }
if (tableObj) {
MOZ_ASSERT(seg.tableIndex == 0);
for (uint32_t i = 0; i < seg.elems.length(); i++) {
- if (!tableObj->setInstance(cx, seg.offset + i, instanceObj))
+ if (!tableObj->setInstance(cx, offset + i, instanceObj))
return false;
}
}
for (uint32_t i = 0; i < seg.elems.length(); i++)
- table.array()[seg.offset + i] = codeSegment.code() + seg.elems[i];
+ table.array()[offset + i] = codeSegment.code() + seg.elems[i];
+
+ prevEnd = offset + seg.elems.length();
}
return true;
}
// asm.js module instantiation supplies its own buffer, but for wasm, create and
// initialize the buffer if one is requested. Either way, the buffer is wrapped
// in a WebAssembly.Memory object which is what the Instance stores.
@@ -464,20 +554,63 @@ Module::instantiateTable(JSContext* cx,
return false;
}
}
return true;
}
static bool
+ExportGlobalValue(JSContext* cx, const GlobalDescVector& globals, uint32_t globalIndex,
+ const ValVector& globalImports, MutableHandleValue jsval)
+{
+ const GlobalDesc& global = globals[globalIndex];
+
+ // Imports are located upfront in the globals array.
+ Val val;
+ switch (global.kind()) {
+ case GlobalKind::Import: val = globalImports[globalIndex]; break;
+ case GlobalKind::Variable: MOZ_CRASH("mutable variables can't be exported");
+ case GlobalKind::Constant: val = global.constantValue(); break;
+ }
+
+ switch (global.type()) {
+ case ValType::I32: {
+ jsval.set(Int32Value(val.i32()));
+ return true;
+ }
+ case ValType::I64: {
+ MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm");
+ RootedObject obj(cx, CreateI64Object(cx, val.i64()));
+ if (!obj)
+ return false;
+ jsval.set(ObjectValue(*obj));
+ return true;
+ }
+ case ValType::F32: {
+ jsval.set(DoubleValue(double(val.f32())));
+ return true;
+ }
+ case ValType::F64: {
+ jsval.set(DoubleValue(val.f64()));
+ return true;
+ }
+ default: {
+ break;
+ }
+ }
+ MOZ_CRASH("unexpected type when creating global exports");
+}
+
+static bool
CreateExportObject(JSContext* cx,
HandleWasmInstanceObject instanceObj,
MutableHandleWasmTableObject tableObj,
HandleWasmMemoryObject memoryObj,
+ const ValVector& globalImports,
const ExportVector& exports,
MutableHandleObject exportObj)
{
const Instance& instance = instanceObj->instance();
const Metadata& metadata = instance.metadata();
if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) {
RootedFunction fun(cx);
@@ -518,30 +651,36 @@ CreateExportObject(JSContext* cx,
}
case DefinitionKind::Memory: {
if (metadata.assumptions.newFormat)
val = ObjectValue(*memoryObj);
else
val = ObjectValue(memoryObj->buffer());
break;
}
+ case DefinitionKind::Global: {
+ if (!ExportGlobalValue(cx, metadata.globals, exp.globalIndex(), globalImports, &val))
+ return false;
+ break;
+ }
}
if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
return false;
}
return true;
}
bool
Module::instantiate(JSContext* cx,
Handle<FunctionVector> funcImports,
HandleWasmTableObject tableImport,
HandleWasmMemoryObject memoryImport,
+ const ValVector& globalImports,
HandleObject instanceProto,
MutableHandleWasmInstanceObject instanceObj) const
{
MOZ_ASSERT(funcImports.length() == metadata_->funcImports.length());
RootedWasmMemoryObject memory(cx, memoryImport);
if (!instantiateMemory(cx, &memory))
return false;
@@ -575,48 +714,49 @@ Module::instantiate(JSContext* cx,
return false;
auto instance = cx->make_unique<Instance>(cx,
Move(codeSegment),
*metadata_,
maybeBytecode,
memory,
Move(tables),
- funcImports);
+ funcImports,
+ globalImports);
if (!instance)
return false;
instanceObj->init(Move(instance));
}
if (!instanceObj->instance().init(cx))
return false;
// Create the export object.
RootedObject exportObj(cx);
RootedWasmTableObject table(cx, tableImport);
- if (!CreateExportObject(cx, instanceObj, &table, memory, exports_, &exportObj))
+ if (!CreateExportObject(cx, instanceObj, &table, memory, globalImports, exports_, &exportObj))
return false;
JSAtom* atom = Atomize(cx, InstanceExportField, strlen(InstanceExportField));
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
RootedValue val(cx, ObjectValue(*exportObj));
if (!JS_DefinePropertyById(cx, instanceObj, id, val, JSPROP_ENUMERATE))
return false;
// Initialize table elements only after the instance is fully initialized
// since the Table object needs to point to a valid instance object. Perform
// initialization as the final step after the instance is fully live since
// it is observable (in the case of an imported Table object).
- if (!initElems(cx, instanceObj, table))
+ if (!initElems(cx, instanceObj, globalImports, table))
return false;
// Done! Notify the Debugger of the new Instance.
Debugger::onNewWasmInstance(cx, instanceObj);
// Call the start function, if there's one. By specification, it does not
// take any arguments nor does it return a value, so just create a dummy
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -21,16 +21,28 @@
#include "asmjs/WasmCode.h"
#include "asmjs/WasmTable.h"
#include "js/TypeDecls.h"
namespace js {
namespace wasm {
+// Creates a JS object containing two fields (low: low 32 bits; high: high 32
+// bits) of a given Int64 value. For testing purposes only.
+
+JSObject*
+CreateI64Object(JSContext* cx, int64_t i64);
+
+// Reads an int64 from a JS object with the same shape as described in the
+// comment above. For testing purposes only.
+
+bool
+ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64);
+
// LinkData contains all the metadata necessary to patch all the locations
// that depend on the absolute address of a CodeSegment.
//
// LinkData is built incrementing by ModuleGenerator and then stored immutably
// in Module.
struct LinkDataCacheablePod
{
@@ -108,28 +120,29 @@ typedef Vector<Import, 0, SystemAllocPol
// ExportVector is built incrementally by ModuleGenerator and then stored
// immutably by Module.
class Export
{
CacheableChars fieldName_;
struct CacheablePod {
DefinitionKind kind_;
- uint32_t funcIndex_;
+ uint32_t index_;
} pod;
public:
Export() = default;
- explicit Export(UniqueChars fieldName, uint32_t funcIndex);
+ explicit Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind);
explicit Export(UniqueChars fieldName, DefinitionKind kind);
const char* fieldName() const { return fieldName_.get(); }
DefinitionKind kind() const { return pod.kind_; }
uint32_t funcIndex() const;
+ uint32_t globalIndex() const;
WASM_DECLARE_SERIALIZABLE(Export)
};
typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
// DataSegment describes the offset of a data segment in the bytecode that is
// to be copied at a given offset into linear memory upon instantiation.
@@ -144,21 +157,21 @@ struct DataSegment
typedef Vector<DataSegment, 0, SystemAllocPolicy> DataSegmentVector;
// ElemSegment represents an element segment in the module where each element's
// function index has been translated to its offset in the code section.
struct ElemSegment
{
uint32_t tableIndex;
- uint32_t offset;
+ InitExpr offset;
Uint32Vector elems;
ElemSegment() = default;
- ElemSegment(uint32_t tableIndex, uint32_t offset, Uint32Vector&& elems)
+ ElemSegment(uint32_t tableIndex, InitExpr offset, Uint32Vector&& elems)
: tableIndex(tableIndex), offset(offset), elems(Move(elems))
{}
WASM_DECLARE_SERIALIZABLE(ElemSegment)
};
typedef Vector<ElemSegment, 0, SystemAllocPolicy> ElemSegmentVector;
@@ -184,17 +197,17 @@ class Module : public RefCounted<Module>
const ElemSegmentVector elemSegments_;
const SharedMetadata metadata_;
const SharedBytes bytecode_;
bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
bool instantiateTable(JSContext* cx, HandleWasmTableObject tableImport,
SharedTableVector* tables) const;
bool initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
- HandleWasmTableObject tableObj) const;
+ const ValVector& globalImports, HandleWasmTableObject tableObj) const;
public:
Module(Bytes&& code,
LinkData&& linkData,
ImportVector&& imports,
ExportVector&& exports,
DataSegmentVector&& dataSegments,
ElemSegmentVector&& elemSegments,
@@ -214,16 +227,17 @@ class Module : public RefCounted<Module>
const ImportVector& imports() const { return imports_; }
// Instantiate this module with the given imports:
bool instantiate(JSContext* cx,
Handle<FunctionVector> funcImports,
HandleWasmTableObject tableImport,
HandleWasmMemoryObject memoryImport,
+ const ValVector& globalImports,
HandleObject instanceProto,
MutableHandleWasmInstanceObject instanceObj) const;
// Structured clone support:
size_t serializedSize() const;
uint8_t* serialize(uint8_t* cursor) const;
static const uint8_t* deserialize(const uint8_t* cursor, RefPtr<Module>* module,
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -81,18 +81,21 @@ class WasmToken
Elem,
Else,
EndOfFile,
Equal,
Error,
Export,
Float,
Func,
+ GetGlobal,
GetLocal,
+ Global,
If,
+ Immutable,
Import,
Index,
Memory,
NegativeZero,
Load,
Local,
Loop,
Module,
@@ -100,16 +103,17 @@ class WasmToken
Nop,
Offset,
OpenParen,
Param,
Resizable,
Result,
Return,
Segment,
+ SetGlobal,
SetLocal,
SignedInteger,
Start,
Store,
Table,
TernaryOpcode,
Text,
Then,
@@ -973,18 +977,22 @@ WasmTokenStream::next()
return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_);
break;
}
break;
}
break;
case 'g':
+ if (consume(u"get_global"))
+ return WasmToken(WasmToken::GetGlobal, begin, cur_);
if (consume(u"get_local"))
return WasmToken(WasmToken::GetLocal, begin, cur_);
+ if (consume(u"global"))
+ return WasmToken(WasmToken::Global, begin, cur_);
break;
case 'i':
if (consume(u"i32")) {
if (!consume(u"."))
return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
switch (*cur_) {
@@ -1261,16 +1269,18 @@ WasmTokenStream::next()
break;
case 'x':
if (consume(u"xor"))
return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_);
break;
}
break;
}
+ if (consume(u"immutable"))
+ return WasmToken(WasmToken::Immutable, begin, cur_);
if (consume(u"import"))
return WasmToken(WasmToken::Import, begin, cur_);
if (consume(u"infinity"))
return WasmToken(WasmToken::Infinity, begin, cur_);
if (consume(u"if"))
return WasmToken(WasmToken::If, begin, cur_);
break;
@@ -1312,16 +1322,18 @@ WasmTokenStream::next()
return WasmToken(WasmToken::Result, begin, cur_);
if (consume(u"return"))
return WasmToken(WasmToken::Return, begin, cur_);
break;
case 's':
if (consume(u"select"))
return WasmToken(WasmToken::TernaryOpcode, Expr::Select, begin, cur_);
+ if (consume(u"set_global"))
+ return WasmToken(WasmToken::SetGlobal, begin, cur_);
if (consume(u"set_local"))
return WasmToken(WasmToken::SetLocal, begin, cur_);
if (consume(u"segment"))
return WasmToken(WasmToken::Segment, begin, cur_);
if (consume(u"start"))
return WasmToken(WasmToken::Start, begin, cur_);
break;
@@ -1836,16 +1848,39 @@ ParseGetLocal(WasmParseContext& c)
{
AstRef local;
if (!c.ts.matchRef(&local, c.error))
return nullptr;
return new(c.lifo) AstGetLocal(local);
}
+static AstGetGlobal*
+ParseGetGlobal(WasmParseContext& c)
+{
+ AstRef local;
+ if (!c.ts.matchRef(&local, c.error))
+ return nullptr;
+ return new(c.lifo) AstGetGlobal(local);
+}
+
+static AstSetGlobal*
+ParseSetGlobal(WasmParseContext& c)
+{
+ AstRef global;
+ if (!c.ts.matchRef(&global, c.error))
+ return nullptr;
+
+ AstExpr* value = ParseExpr(c);
+ if (!value)
+ return nullptr;
+
+ return new(c.lifo) AstSetGlobal(global, *value);
+}
+
static AstSetLocal*
ParseSetLocal(WasmParseContext& c)
{
AstRef local;
if (!c.ts.matchRef(&local, c.error))
return nullptr;
AstExpr* value = ParseExpr(c);
@@ -2171,24 +2206,28 @@ ParseExprInsideParens(WasmParseContext&
case WasmToken::ComparisonOpcode:
return ParseComparisonOperator(c, token.expr());
case WasmToken::Const:
return ParseConst(c, token);
case WasmToken::ConversionOpcode:
return ParseConversionOperator(c, token.expr());
case WasmToken::If:
return ParseIf(c);
+ case WasmToken::GetGlobal:
+ return ParseGetGlobal(c);
case WasmToken::GetLocal:
return ParseGetLocal(c);
case WasmToken::Load:
return ParseLoad(c, token.expr());
case WasmToken::Loop:
return ParseBlock(c, Expr::Loop);
case WasmToken::Return:
return ParseReturn(c);
+ case WasmToken::SetGlobal:
+ return ParseSetGlobal(c);
case WasmToken::SetLocal:
return ParseSetLocal(c);
case WasmToken::Store:
return ParseStore(c, token.expr());
case WasmToken::TernaryOpcode:
return ParseTernaryOperator(c, token.expr());
case WasmToken::UnaryOpcode:
return ParseUnaryOperator(c, token.expr());
@@ -2418,16 +2457,30 @@ ParseStartFunc(WasmParseContext& c, Wasm
if (!module->setStartFunc(AstStartFunc(func))) {
c.ts.generateError(token, c.error);
return false;
}
return true;
}
+static bool
+ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags)
+{
+ if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
+ return false;
+
+ // Mutable by default.
+ *flags = 0x1;
+ if (c.ts.getIf(WasmToken::Immutable))
+ *flags = 0x0;
+
+ return true;
+}
+
static AstImport*
ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
{
AstName name = c.ts.getIfName();
WasmToken moduleName;
if (!c.ts.match(WasmToken::Text, &moduleName, c.error))
return nullptr;
@@ -2453,16 +2506,26 @@ ParseImport(WasmParseContext& c, bool ne
AstResizable table;
if (!ParseResizable(c, &table))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
DefinitionKind::Table, table);
}
+ if (c.ts.getIf(WasmToken::Global)) {
+ WasmToken typeToken;
+ uint32_t flags = 0;
+ if (!ParseGlobalType(c, &typeToken, &flags))
+ return nullptr;
+ if (!c.ts.match(WasmToken::CloseParen, c.error))
+ return nullptr;
+ return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
+ AstGlobal(AstName(), typeToken.valueType(), flags));
+ }
}
if (c.ts.getIf(WasmToken::Type)) {
if (!c.ts.matchRef(&sigRef, c.error))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
} else {
@@ -2489,23 +2552,31 @@ ParseExport(WasmParseContext& c)
{
WasmToken name;
if (!c.ts.match(WasmToken::Text, &name, c.error))
return nullptr;
WasmToken exportee = c.ts.get();
switch (exportee.kind()) {
case WasmToken::Index:
- return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index()));
+ return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
+ AstRef(AstName(), exportee.index()));
case WasmToken::Name:
- return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex));
+ return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
+ AstRef(exportee.name(), AstNoIndex));
case WasmToken::Table:
return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
case WasmToken::Memory:
return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
+ case WasmToken::Global: {
+ AstRef ref;
+ if (!c.ts.matchRef(&ref, c.error))
+ return nullptr;
+ return new(c.lifo) AstExport(name.text(), DefinitionKind::Global, ref);
+ }
default:
break;
}
c.ts.generateError(exportee, c.error);
return nullptr;
}
@@ -2536,36 +2607,57 @@ ParseTable(WasmParseContext& c, WasmToke
return false;
}
if (!module->setTable(AstResizable(elems.length(), Some<uint32_t>(elems.length())))) {
c.ts.generateError(token, c.error);
return false;
}
- AstElemSegment* segment = new(c.lifo) AstElemSegment(0, Move(elems));
+ auto* zero = new(c.lifo) AstConst(Val(uint32_t(0)));
+ if (!zero)
+ return false;
+
+ AstElemSegment* segment = new(c.lifo) AstElemSegment(zero, Move(elems));
return segment && module->append(segment);
}
static AstElemSegment*
ParseElemSegment(WasmParseContext& c)
{
- WasmToken offset;
- if (!c.ts.match(WasmToken::Index, &offset, c.error))
+ AstExpr* offset = ParseExpr(c);
+ if (!offset)
return nullptr;
AstRefVector elems(c.lifo);
AstRef elem;
while (c.ts.getIfRef(&elem)) {
if (!elems.append(elem))
return nullptr;
}
- return new(c.lifo) AstElemSegment(offset.index(), Move(elems));
+ return new(c.lifo) AstElemSegment(offset, Move(elems));
+}
+
+static AstGlobal*
+ParseGlobal(WasmParseContext& c)
+{
+ AstName name = c.ts.getIfName();
+
+ WasmToken typeToken;
+ uint32_t flags = 0;
+ if (!ParseGlobalType(c, &typeToken, &flags))
+ return nullptr;
+
+ AstExpr* init = ParseExpr(c);
+ if (!init)
+ return nullptr;
+
+ return new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
}
static AstModule*
ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars* error)
{
WasmParseContext c(text, lifo, error);
if (!c.ts.match(WasmToken::OpenParen, c.error))
@@ -2592,16 +2684,22 @@ ParseModule(const char16_t* text, bool n
return nullptr;
break;
}
case WasmToken::Memory: {
if (!ParseMemory(c, section, module))
return nullptr;
break;
}
+ case WasmToken::Global: {
+ AstGlobal* global = ParseGlobal(c);
+ if (!global || !module->append(global))
+ return nullptr;
+ break;
+ }
case WasmToken::Data: {
AstDataSegment* segment = ParseDataSegment(c);
if (!segment || !module->append(segment))
return nullptr;
break;
}
case WasmToken::Import: {
AstImport* imp = ParseImport(c, newFormat, module);
@@ -2653,16 +2751,17 @@ ParseModule(const char16_t* text, bool n
// wasm name resolution
namespace {
class Resolver
{
UniqueChars* error_;
AstNameMap varMap_;
+ AstNameMap globalMap_;
AstNameMap sigMap_;
AstNameMap funcMap_;
AstNameMap importMap_;
AstNameVector targetStack_;
bool registerName(AstNameMap& map, AstName name, size_t index) {
AstNameMap::AddPtr p = map.lookupForAdd(name);
if (!p) {
@@ -2698,23 +2797,28 @@ class Resolver
error_->reset(JS_smprintf("%s label '%hs' not found", kind, nameWithNull.begin()));
return false;
}
public:
explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
: error_(error),
varMap_(lifo),
+ globalMap_(lifo),
sigMap_(lifo),
funcMap_(lifo),
importMap_(lifo),
targetStack_(lifo)
{}
bool init() {
- return sigMap_.init() && funcMap_.init() && importMap_.init() && varMap_.init();
+ return sigMap_.init() &&
+ funcMap_.init() &&
+ importMap_.init() &&
+ varMap_.init() &&
+ globalMap_.init();
}
void beginFunc() {
varMap_.clear();
MOZ_ASSERT(targetStack_.empty());
}
bool registerSigName(AstName name, size_t index) {
return name.empty() || registerName(sigMap_, name, index);
}
@@ -2722,16 +2826,19 @@ class Resolver
return name.empty() || registerName(funcMap_, name, index);
}
bool registerImportName(AstName name, size_t index) {
return name.empty() || registerName(importMap_, name, index);
}
bool registerVarName(AstName name, size_t index) {
return name.empty() || registerName(varMap_, name, index);
}
+ bool registerGlobalName(AstName name, size_t index) {
+ return name.empty() || registerName(globalMap_, name, index);
+ }
bool pushTarget(AstName name) {
return targetStack_.append(name);
}
void popTarget(AstName name) {
MOZ_ASSERT(targetStack_.back() == name);
targetStack_.popBack();
}
@@ -2750,16 +2857,21 @@ class Resolver
return failResolveLabel("import", ref.name());
return true;
}
bool resolveLocal(AstRef& ref) {
if (!ref.name().empty() && !resolveRef(varMap_, ref))
return failResolveLabel("local", ref.name());
return true;
}
+ bool resolveGlobal(AstRef& ref) {
+ if (!ref.name().empty() && !resolveRef(globalMap_, ref))
+ return failResolveLabel("global", ref.name());
+ return true;
+ }
bool resolveBranchTarget(AstRef& ref) {
if (ref.name().empty())
return true;
for (size_t i = 0, e = targetStack_.length(); i < e; i++) {
if (targetStack_[e - i - 1] == ref.name()) {
ref.setIndex(i);
return true;
}
@@ -2883,16 +2995,34 @@ ResolveSetLocal(Resolver& r, AstSetLocal
if (!r.resolveLocal(sl.local()))
return false;
return true;
}
static bool
+ResolveGetGlobal(Resolver& r, AstGetGlobal& gl)
+{
+ return r.resolveGlobal(gl.global());
+}
+
+static bool
+ResolveSetGlobal(Resolver& r, AstSetGlobal& sl)
+{
+ if (!ResolveExpr(r, sl.value()))
+ return false;
+
+ if (!r.resolveGlobal(sl.global()))
+ return false;
+
+ return true;
+}
+
+static bool
ResolveUnaryOperator(Resolver& r, AstUnaryOperator& b)
{
return ResolveExpr(r, *b.op());
}
static bool
ResolveBinaryOperator(Resolver& r, AstBinaryOperator& b)
{
@@ -3001,24 +3131,28 @@ ResolveExpr(Resolver& r, AstExpr& expr)
case AstExprKind::CallIndirect:
return ResolveCallIndirect(r, expr.as<AstCallIndirect>());
case AstExprKind::ComparisonOperator:
return ResolveComparisonOperator(r, expr.as<AstComparisonOperator>());
case AstExprKind::Const:
return true;
case AstExprKind::ConversionOperator:
return ResolveConversionOperator(r, expr.as<AstConversionOperator>());
+ case AstExprKind::GetGlobal:
+ return ResolveGetGlobal(r, expr.as<AstGetGlobal>());
case AstExprKind::GetLocal:
return ResolveGetLocal(r, expr.as<AstGetLocal>());
case AstExprKind::If:
return ResolveIfElse(r, expr.as<AstIf>());
case AstExprKind::Load:
return ResolveLoad(r, expr.as<AstLoad>());
case AstExprKind::Return:
return ResolveReturn(r, expr.as<AstReturn>());
+ case AstExprKind::SetGlobal:
+ return ResolveSetGlobal(r, expr.as<AstSetGlobal>());
case AstExprKind::SetLocal:
return ResolveSetLocal(r, expr.as<AstSetLocal>());
case AstExprKind::Store:
return ResolveStore(r, expr.as<AstStore>());
case AstExprKind::BranchTable:
return ResolveBranchTable(r, expr.as<AstBranchTable>());
case AstExprKind::TernaryOperator:
return ResolveTernaryOperator(r, expr.as<AstTernaryOperator>());
@@ -3028,18 +3162,17 @@ ResolveExpr(Resolver& r, AstExpr& expr)
MOZ_CRASH("Bad expr kind");
}
static bool
ResolveFunc(Resolver& r, AstFunc& func)
{
r.beginFunc();
- size_t numVars = func.locals().length();
- for (size_t i = 0; i < numVars; i++) {
+ for (size_t i = 0; i < func.locals().length(); i++) {
if (!r.registerVarName(func.locals()[i], i))
return r.fail("duplicate var");
}
for (AstExpr* expr : func.body()) {
if (!ResolveExpr(r, *expr))
return false;
}
@@ -3073,49 +3206,75 @@ ResolveModule(LifoAlloc& lifo, AstModule
for (AstElemSegment* seg : module->elemSegments()) {
for (AstRef& ref : seg->elems()) {
if (!r.resolveFunction(ref))
return false;
}
}
size_t numImports = module->imports().length();
+ size_t lastGlobalIndex = 0;
for (size_t i = 0; i < numImports; i++) {
AstImport* imp = module->imports()[i];
- if (!r.registerImportName(imp->name(), i))
- return r.fail("duplicate import");
-
switch (imp->kind()) {
case DefinitionKind::Function:
+ if (!r.registerImportName(imp->name(), i))
+ return r.fail("duplicate import");
if (!r.resolveSignature(imp->funcSig()))
return false;
break;
+ case DefinitionKind::Global:
+ if (!r.registerGlobalName(imp->name(), lastGlobalIndex++))
+ return r.fail("duplicate import");
+ break;
case DefinitionKind::Memory:
case DefinitionKind::Table:
break;
}
}
+ const AstGlobalVector& globals = module->globals();
+ for (const AstGlobal* global : globals) {
+ if (!r.registerGlobalName(global->name(), lastGlobalIndex++))
+ return r.fail("duplicate import");
+ if (global->hasInit() && !ResolveExpr(r, global->init()))
+ return false;
+ }
+
for (AstExport* export_ : module->exports()) {
- if (export_->kind() != DefinitionKind::Function)
- continue;
- if (!r.resolveFunction(export_->func()))
- return false;
+ switch (export_->kind()) {
+ case DefinitionKind::Function:
+ if (!r.resolveFunction(export_->ref()))
+ return false;
+ break;
+ case DefinitionKind::Global:
+ if (!r.resolveGlobal(export_->ref()))
+ return false;
+ break;
+ case DefinitionKind::Table:
+ case DefinitionKind::Memory:
+ break;
+ }
}
for (AstFunc* func : module->funcs()) {
if (!ResolveFunc(r, *func))
return false;
}
if (module->hasStartFunc()) {
if (!r.resolveFunction(module->startFunc().func()))
return false;
}
+ for (AstElemSegment* segment : module->elemSegments()) {
+ if (!ResolveExpr(r, *segment->offset()))
+ return false;
+ }
+
return true;
}
/*****************************************************************************/
// wasm function body serialization
static bool
EncodeExpr(Encoder& e, AstExpr& expr);
@@ -3257,16 +3416,31 @@ static bool
EncodeSetLocal(Encoder& e, AstSetLocal& sl)
{
return EncodeExpr(e, sl.value()) &&
e.writeExpr(Expr::SetLocal) &&
e.writeVarU32(sl.local().index());
}
static bool
+EncodeGetGlobal(Encoder& e, AstGetGlobal& gg)
+{
+ return e.writeExpr(Expr::GetGlobal) &&
+ e.writeVarU32(gg.global().index());
+}
+
+static bool
+EncodeSetGlobal(Encoder& e, AstSetGlobal& sg)
+{
+ return EncodeExpr(e, sg.value()) &&
+ e.writeExpr(Expr::SetGlobal) &&
+ e.writeVarU32(sg.global().index());
+}
+
+static bool
EncodeUnaryOperator(Encoder& e, AstUnaryOperator& b)
{
return EncodeExpr(e, *b.op()) &&
e.writeExpr(b.expr());
}
static bool
EncodeBinaryOperator(Encoder& e, AstBinaryOperator& b)
@@ -3422,24 +3596,28 @@ EncodeExpr(Encoder& e, AstExpr& expr)
case AstExprKind::ComparisonOperator:
return EncodeComparisonOperator(e, expr.as<AstComparisonOperator>());
case AstExprKind::Const:
return EncodeConst(e, expr.as<AstConst>());
case AstExprKind::ConversionOperator:
return EncodeConversionOperator(e, expr.as<AstConversionOperator>());
case AstExprKind::GetLocal:
return EncodeGetLocal(e, expr.as<AstGetLocal>());
+ case AstExprKind::GetGlobal:
+ return EncodeGetGlobal(e, expr.as<AstGetGlobal>());
case AstExprKind::If:
return EncodeIf(e, expr.as<AstIf>());
case AstExprKind::Load:
return EncodeLoad(e, expr.as<AstLoad>());
case AstExprKind::Return:
return EncodeReturn(e, expr.as<AstReturn>());
case AstExprKind::SetLocal:
return EncodeSetLocal(e, expr.as<AstSetLocal>());
+ case AstExprKind::SetGlobal:
+ return EncodeSetGlobal(e, expr.as<AstSetGlobal>());
case AstExprKind::Store:
return EncodeStore(e, expr.as<AstStore>());
case AstExprKind::BranchTable:
return EncodeBranchTable(e, expr.as<AstBranchTable>());
case AstExprKind::TernaryOperator:
return EncodeTernaryOperator(e, expr.as<AstTernaryOperator>());
case AstExprKind::UnaryOperator:
return EncodeUnaryOperator(e, expr.as<AstUnaryOperator>());
@@ -3564,16 +3742,23 @@ EncodeImport(Encoder& e, bool newFormat,
if (!e.writeVarU32(uint32_t(imp.kind())))
return false;
switch (imp.kind()) {
case DefinitionKind::Function:
if (!e.writeVarU32(imp.funcSig().index()))
return false;
break;
+ case DefinitionKind::Global:
+ MOZ_ASSERT(!imp.global().hasInit());
+ if (!e.writeValType(imp.global().type()))
+ return false;
+ if (!e.writeVarU32(imp.global().flags()))
+ return false;
+ break;
case DefinitionKind::Table:
case DefinitionKind::Memory:
if (!EncodeResizable(e, imp.resizable()))
return false;
break;
}
return true;
@@ -3636,40 +3821,69 @@ EncodeMemorySection(Encoder& e, bool new
return false;
}
e.finishSection(offset);
return true;
}
static bool
+EncodeGlobalSection(Encoder& e, AstModule& module)
+{
+ size_t offset;
+ if (!e.startSection(GlobalSectionId, &offset))
+ return false;
+
+ const AstGlobalVector& globals = module.globals();
+
+ if (!e.writeVarU32(globals.length()))
+ return false;
+
+ for (const AstGlobal* global : globals) {
+ MOZ_ASSERT(global->hasInit());
+ if (!e.writeValType(global->type()))
+ return false;
+ if (!e.writeVarU32(global->flags()))
+ return false;
+ if (!EncodeExpr(e, global->init()))
+ return false;
+ if (!e.writeExpr(Expr::End))
+ return false;
+ }
+
+ e.finishSection(offset);
+ return true;
+}
+
+static bool
EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
{
if (!newFormat) {
if (exp.kind() != DefinitionKind::Function)
return true;
- if (!e.writeVarU32(exp.func().index()))
+ if (!e.writeVarU32(exp.ref().index()))
return false;
if (!EncodeBytes(e, exp.name()))
return false;
return true;
}
if (!EncodeBytes(e, exp.name()))
return false;
if (!e.writeVarU32(uint32_t(exp.kind())))
return false;
switch (exp.kind()) {
case DefinitionKind::Function:
- if (!e.writeVarU32(exp.func().index()))
+ case DefinitionKind::Global:
+ if (!e.writeVarU32(exp.ref().index()))
return false;
break;
case DefinitionKind::Table:
case DefinitionKind::Memory:
if (!e.writeVarU32(0))
return false;
break;
}
@@ -3863,19 +4077,19 @@ EncodeDataSection(Encoder& e, bool newFo
}
static bool
EncodeElemSegment(Encoder& e, AstElemSegment& segment)
{
if (!e.writeVarU32(0)) // table index
return false;
- if (!e.writeExpr(Expr::I32Const))
+ if (!EncodeExpr(e, *segment.offset()))
return false;
- if (!e.writeVarU32(segment.offset()))
+ if (!e.writeExpr(Expr::End))
return false;
if (!e.writeVarU32(segment.elems().length()))
return false;
for (const AstRef& elem : segment.elems()) {
if (!e.writeVarU32(elem.index()))
return false;
@@ -3927,16 +4141,19 @@ EncodeModule(AstModule& module, bool new
return false;
if (!EncodeTableSection(e, newFormat, module))
return false;
if (!EncodeMemorySection(e, newFormat, module))
return false;
+ if (!EncodeGlobalSection(e, module))
+ return false;
+
if (!EncodeExportSection(e, newFormat, module))
return false;
if (!EncodeStartSection(e, module))
return false;
if (!EncodeCodeSection(e, module))
return false;
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -32,17 +32,17 @@
#include "vm/Stack-inl.h"
using namespace js;
using namespace js::jit;
using namespace js::wasm;
void
-Val::writePayload(uint8_t* dst)
+Val::writePayload(uint8_t* dst) const
{
switch (type_) {
case ValType::I32:
case ValType::F32:
memcpy(dst, &u.i32_, sizeof(u.i32_));
return;
case ValType::I64:
case ValType::F64:
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -356,19 +356,21 @@ class Val
MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
return u.i32x4_;
}
const F32x4& f32x4() const {
MOZ_ASSERT(type_ == ValType::F32x4);
return u.f32x4_;
}
- void writePayload(uint8_t* dst);
+ void writePayload(uint8_t* dst) const;
};
+typedef Vector<Val, 0, SystemAllocPolicy> ValVector;
+
// The Sig class represents a WebAssembly function signature which takes a list
// of value types and returns an expression type. The engine uses two in-memory
// representations of the argument Vector's memory (when elements do not fit
// inline): normal malloc allocation (via SystemAllocPolicy) and allocation in
// a LifoAlloc (via LifoAllocPolicy). The former Sig objects can have any
// lifetime since they own the memory. The latter Sig objects must not outlive
// the associated LifoAlloc mark/release interval (which is currently the
// duration of module validation+compilation). Thus, long-lived objects like
@@ -408,16 +410,152 @@ class Sig
struct SigHashPolicy
{
typedef const Sig& Lookup;
static HashNumber hash(Lookup sig) { return sig.hash(); }
static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
};
+// An InitExpr describes a deferred initializer expression, used to initialize
+// a global or a table element offset. Such expressions are created during
+// decoding and actually executed on module instantiation.
+
+class InitExpr
+{
+ public:
+ enum class Kind {
+ Constant,
+ GetGlobal
+ };
+
+ private:
+ Kind kind_;
+ union {
+ Val val_;
+ struct {
+ uint32_t index_;
+ ValType type_;
+ } global;
+ } u;
+
+ public:
+ InitExpr() = default;
+
+ explicit InitExpr(Val val) : kind_(Kind::Constant) {
+ u.val_ = val;
+ }
+
+ explicit InitExpr(uint32_t globalIndex, ValType type) : kind_(Kind::GetGlobal) {
+ u.global.index_ = globalIndex;
+ u.global.type_ = type;
+ }
+
+ Kind kind() const { return kind_; }
+
+ bool isVal() const { return kind() == Kind::Constant; }
+ Val val() const { MOZ_ASSERT(isVal()); return u.val_; }
+
+ uint32_t globalIndex() const { MOZ_ASSERT(kind() == Kind::GetGlobal); return u.global.index_; }
+
+ ValType type() const {
+ switch (kind()) {
+ case Kind::Constant: return u.val_.type();
+ case Kind::GetGlobal: return u.global.type_;
+ }
+ MOZ_CRASH("unexpected initExpr type");
+ }
+};
+
+// A GlobalDesc describes a single global variable. Currently, asm.js and wasm
+// exposes mutable and immutable private globals, but can't import nor export
+// mutable globals.
+
+enum class GlobalKind
+{
+ Import,
+ Constant,
+ Variable
+};
+
+class GlobalDesc
+{
+ union {
+ struct {
+ union {
+ InitExpr initial_;
+ struct {
+ ValType type_;
+ uint32_t index_;
+ } import;
+ } val;
+ unsigned offset_;
+ bool isMutable_;
+ } var;
+ Val cst_;
+ } u;
+ GlobalKind kind_;
+
+ public:
+ GlobalDesc() = default;
+
+ explicit GlobalDesc(InitExpr initial, bool isMutable)
+ : kind_((isMutable || !initial.isVal()) ? GlobalKind::Variable : GlobalKind::Constant)
+ {
+ if (isVariable()) {
+ u.var.val.initial_ = initial;
+ u.var.isMutable_ = isMutable;
+ u.var.offset_ = UINT32_MAX;
+ } else {
+ u.cst_ = initial.val();
+ }
+ }
+
+ explicit GlobalDesc(ValType type, bool isMutable, uint32_t importIndex)
+ : kind_(GlobalKind::Import)
+ {
+ u.var.val.import.type_ = type;
+ u.var.val.import.index_ = importIndex;
+ u.var.isMutable_ = isMutable;
+ u.var.offset_ = UINT32_MAX;
+ }
+
+ void setOffset(unsigned offset) {
+ MOZ_ASSERT(!isConstant());
+ MOZ_ASSERT(u.var.offset_ == UINT32_MAX);
+ u.var.offset_ = offset;
+ }
+ unsigned offset() const {
+ MOZ_ASSERT(!isConstant());
+ MOZ_ASSERT(u.var.offset_ != UINT32_MAX);
+ return u.var.offset_;
+ }
+
+ GlobalKind kind() const { return kind_; }
+ bool isVariable() const { return kind_ == GlobalKind::Variable; }
+ bool isConstant() const { return kind_ == GlobalKind::Constant; }
+ bool isImport() const { return kind_ == GlobalKind::Import; }
+
+ bool isMutable() const { return !isConstant() && u.var.isMutable_; }
+ Val constantValue() const { MOZ_ASSERT(isConstant()); return u.cst_; }
+ const InitExpr& initExpr() const { MOZ_ASSERT(isVariable()); return u.var.val.initial_; }
+ uint32_t importIndex() const { MOZ_ASSERT(isImport()); return u.var.val.import.index_; }
+
+ ValType type() const {
+ switch (kind_) {
+ case GlobalKind::Import: return u.var.val.import.type_;
+ case GlobalKind::Variable: return u.var.val.initial_.type();
+ case GlobalKind::Constant: return u.cst_.type();
+ }
+ MOZ_CRASH("unexpected global kind");
+ }
+};
+
+typedef Vector<GlobalDesc, 0, SystemAllocPolicy> GlobalDescVector;
+
// SigIdDesc describes a signature id that can be used by call_indirect and
// table-entry prologues to structurally compare whether the caller and callee's
// signatures *structurally* match. To handle the general case, a Sig is
// allocated and stored in a process-wide hash table, so that pointer equality
// implies structural equality. As an optimization for the 99% case where the
// Sig has a small number of parameters, the Sig is bit-packed into a uint32
// immediate value so that integer equality implies structural equality. Both
// cases can be handled with a single comparison by always setting the LSB for
@@ -463,31 +601,16 @@ struct SigWithId : Sig
void operator=(Sig&& rhs) { Sig::operator=(Move(rhs)); }
WASM_DECLARE_SERIALIZABLE(SigWithId)
};
typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector;
typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector;
-// A GlobalDesc describes a single global variable. Currently, globals are only
-// exposed through asm.js.
-
-struct GlobalDesc
-{
- ValType type;
- unsigned globalDataOffset;
- bool isConst;
- GlobalDesc(ValType type, unsigned offset, bool isConst)
- : type(type), globalDataOffset(offset), isConst(isConst)
- {}
-};
-
-typedef Vector<GlobalDesc, 0, SystemAllocPolicy> GlobalDescVector;
-
// The (,Profiling,Func)Offsets classes are used to record the offsets of
// different key points in a CodeRange during compilation.
struct Offsets
{
explicit Offsets(uint32_t begin = 0, uint32_t end = 0)
: begin(begin), end(end)
{}
@@ -935,16 +1058,17 @@ static const uint64_t MappedSize = 2 * U
static const unsigned ActivationGlobalDataOffset = 0;
static const unsigned HeapGlobalDataOffset = ActivationGlobalDataOffset + sizeof(void*);
static const unsigned NaN64GlobalDataOffset = HeapGlobalDataOffset + sizeof(void*);
static const unsigned NaN32GlobalDataOffset = NaN64GlobalDataOffset + sizeof(double);
static const unsigned InitialGlobalDataBytes = NaN32GlobalDataOffset + sizeof(float);
static const unsigned MaxSigs = 4 * 1024;
static const unsigned MaxFuncs = 512 * 1024;
+static const unsigned MaxGlobals = 4 * 1024;
static const unsigned MaxLocals = 64 * 1024;
static const unsigned MaxImports = 64 * 1024;
static const unsigned MaxExports = 64 * 1024;
static const unsigned MaxTables = 4 * 1024;
static const unsigned MaxTableElems = 128 * 1024;
static const unsigned MaxDataSegments = 64 * 1024;
static const unsigned MaxElemSegments = 64 * 1024;
static const unsigned MaxArgsPerFunc = 4 * 1024;
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -31,18 +31,19 @@ function jsify(wasmVal) {
return wasmVal;
}
// Assert that the expected value is equal to the int64 value, as passed by
// Baldr with --wasm-extra-tests {low: int32, high: int32}.
// - if the expected value is in the int32 range, it can be just a number.
// - otherwise, an object with the properties "high" and "low".
function assertEqI64(observed, expect) {
- assertEq(typeof observed, 'object');
- assertEq(typeof expect === 'object' || typeof expect === 'number', true);
+ assertEq(typeof observed, 'object', "observed must be an i64 object");
+ assertEq(typeof expect === 'object' || typeof expect === 'number', true,
+ "expect must be an i64 object or number");
let {low, high} = observed;
if (typeof expect === 'number') {
assertEq(expect, expect | 0, "in int32 range");
assertEq(low, expect | 0, "low 32 bits don't match");
assertEq(high, expect < 0 ? -1 : 0, "high 32 bits don't match"); // sign extension
} else {
assertEq(typeof expect.low, 'number');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -0,0 +1,209 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+const { Instance, Module } = WebAssembly;
+const evalText = (txt, imports = {}) => new Instance(new Module(wasmTextToBinary(txt, 'new-format')), imports).exports;
+
+// Locally-defined globals
+assertErrorMessage(() => evalText(`(module (global))`), SyntaxError, /parsing/);
+assertErrorMessage(() => evalText(`(module (global i32))`), SyntaxError, /parsing/);
+assertErrorMessage(() => evalText(`(module (global i32 immutable))`), SyntaxError, /parsing/);
+
+// Initializer expressions.
+assertErrorMessage(() => evalText(`(module (global i32 (f32.const 13.37)))`), TypeError, /type mismatch/);
+assertErrorMessage(() => evalText(`(module (global f64 (f32.const 13.37)))`), TypeError, /type mismatch/);
+assertErrorMessage(() => evalText(`(module (global i32 (i32.add (i32.const 13) (i32.const 37))))`), TypeError, /failed to read end/);
+
+assertErrorMessage(() => evalText(`(module (global i32 (get_global 0)))`), TypeError, /out of range/);
+assertErrorMessage(() => evalText(`(module (global i32 (get_global 1)) (global i32 immutable (i32.const 1)))`), TypeError, /out of range/);
+
+// Test a well-defined global section.
+function testInner(type, initialValue, nextValue, coercion, assertFunc = assertEq)
+{
+ var module = evalText(`(module
+ (global ${type} (${type}.const ${initialValue}))
+ (global ${type} immutable (${type}.const ${initialValue}))
+
+ (func $get (result ${type}) (get_global 0))
+ (func $set (param ${type}) (set_global 0 (get_local 0)))
+
+ (func $get_cst (result ${type}) (get_global 1))
+
+ (export "get" $get)
+ (export "get_cst" $get_cst)
+
+ (export "set" $set)
+ )`);
+
+ assertFunc(module.get(), coercion(initialValue));
+ assertEq(module.set(coercion(nextValue)), undefined);
+ assertFunc(module.get(), coercion(nextValue));
+
+ assertFunc(module.get_cst(), coercion(initialValue));
+}
+
+testInner('i32', 13, 37, x => x|0);
+testInner('f32', 13.37, 0.1989, Math.fround);
+testInner('f64', 13.37, 0.1989, x => +x);
+
+// Semantic errors.
+assertErrorMessage(() => evalText(`(module (global i32 (i32.const 1337)) (func (set_global 1 (i32.const 0))))`), TypeError, /out of range/);
+assertErrorMessage(() => evalText(`(module (global i32 immutable (i32.const 1337)) (func (set_global 0 (i32.const 0))))`), TypeError, /can't write an immutable global/);
+
+// Big module with many variables: test that setting one doesn't overwrite the
+// other ones.
+function get_set(i, type) { return `
+ (func $get_${i} (result ${type}) (get_global ${i}))
+ (func $set_${i} (param ${type}) (set_global ${i} (get_local 0)))
+`
+}
+
+var module = evalText(`(module
+ (global i32 (i32.const 42))
+ (global i32 (i32.const 10))
+ (global f32 (f32.const 13.37))
+ (global f64 (f64.const 13.37))
+ (global i32 (i32.const -18))
+
+ ${get_set(0, 'i32')}
+ ${get_set(1, 'i32')}
+ ${get_set(2, 'f32')}
+ ${get_set(3, 'f64')}
+ ${get_set(4, 'i32')}
+
+ (export "get0" $get_0) (export "set0" $set_0)
+ (export "get1" $get_1) (export "set1" $set_1)
+ (export "get2" $get_2) (export "set2" $set_2)
+ (export "get3" $get_3) (export "set3" $set_3)
+ (export "get4" $get_4) (export "set4" $set_4)
+)`);
+
+let values = [42, 10, Math.fround(13.37), 13.37, -18];
+let nextValues = [13, 37, Math.fround(-17.89), 9.3, -13];
+for (let i = 0; i < 5; i++) {
+ assertEq(module[`get${i}`](), values[i]);
+ assertEq(module[`set${i}`](nextValues[i]), undefined);
+ assertEq(module[`get${i}`](), nextValues[i]);
+ for (let j = 0; j < 5; j++) {
+ if (i === j)
+ continue;
+ assertEq(module[`get${j}`](), values[j]);
+ }
+ assertEq(module[`set${i}`](values[i]), undefined);
+ assertEq(module[`get${i}`](), values[i]);
+}
+
+// Initializer expressions can also be used in elem section initializers.
+assertErrorMessage(() => evalText(`(module (import "globals" "a" (global f32 immutable)) (table (resizable 4)) (elem (get_global 0) $f) (func $f))`), TypeError, /type mismatch/);
+
+module = evalText(`(module
+ (import "globals" "a" (global i32 immutable))
+ (table (resizable 4))
+ (elem (get_global 0) $f)
+ (func $f)
+ (export "f" $f)
+ (export "tbl" table)
+)`, {
+ globals: {
+ a: 1
+ }
+});
+assertEq(module.f, module.tbl.get(1));
+
+// Import/export rules.
+assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i32)))`), TypeError, /can't import.* mutable globals in the MVP/);
+assertErrorMessage(() => evalText(`(module (global i32 (i32.const 42)) (export "" global 0))`), TypeError, /can't .*export mutable globals in the MVP/);
+
+// Import/export semantics.
+module = evalText(`(module
+ (import $g "globals" "x" (global i32 immutable))
+ (func $get (result i32) (get_global $g))
+ (export "getter" $get)
+ (export "value" global 0)
+)`, { globals: {x: 42} });
+
+assertEq(module.getter(), 42);
+assertEq(module.value, 42);
+
+// Imported globals and locally defined globals use the same index space.
+module = evalText(`(module
+ (import "globals" "x" (global i32 immutable))
+ (global i32 immutable (i32.const 1337))
+ (export "imported" global 0)
+ (export "defined" global 1)
+)`, { globals: {x: 42} });
+
+assertEq(module.imported, 42);
+assertEq(module.defined, 1337);
+
+// Initializer expressions can reference an imported immutable global.
+assertErrorMessage(() => evalText(`(module (global f32 immutable (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
+assertErrorMessage(() => evalText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
+assertErrorMessage(() => evalText(`(module (global i32 (i32.const 0)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
+
+assertErrorMessage(() => evalText(`(module (import "globals" "a" (global f32 immutable)) (global i32 (get_global 0)))`), TypeError, /type mismatch/);
+
+function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) {
+ var module = evalText(`(module
+ (import "globals" "a" (global ${type} immutable))
+
+ (global ${type} (get_global 0))
+ (global ${type} immutable (get_global 0))
+
+ (func $get0 (result ${type}) (get_global 0))
+
+ (func $get1 (result ${type}) (get_global 1))
+ (func $set1 (param ${type}) (set_global 1 (get_local 0)))
+
+ (func $get_cst (result ${type}) (get_global 2))
+
+ (export "get0" $get0)
+ (export "get1" $get1)
+ (export "get_cst" $get_cst)
+
+ (export "set1" $set1)
+ )`, {
+ globals: {
+ a: coercion(initialValue)
+ }
+ });
+
+ assertFunc(module.get0(), coercion(initialValue));
+ assertFunc(module.get1(), coercion(initialValue));
+
+ assertEq(module.set1(coercion(nextValue)), undefined);
+ assertFunc(module.get1(), coercion(nextValue));
+ assertFunc(module.get0(), coercion(initialValue));
+
+ assertFunc(module.get_cst(), coercion(initialValue));
+}
+
+testInitExpr('i32', 13, 37, x => x|0);
+testInitExpr('f32', 13.37, 0.1989, Math.fround);
+testInitExpr('f64', 13.37, 0.1989, x => +x);
+
+// Int64.
+if (hasI64()) {
+ assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /can't import.* an Int64 global/);
+ assertErrorMessage(() => evalText(`(module (global i64 immutable (i64.const 42)) (export "" global 0))`), TypeError, /can't .*export an Int64 global/);
+
+ setJitCompilerOption('wasm.test-mode', 1);
+ testInner('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
+ testInitExpr('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
+
+ module = evalText(`(module
+ (import "globals" "x" (global i64 immutable))
+ (global i64 immutable (i64.const 0xFAFADADABABA))
+ (export "imported" global 0)
+ (export "defined" global 1)
+ )`, { globals: {x: createI64('0x1234567887654321')} });
+
+ assertEqI64(module.imported, createI64('0x1234567887654321'));
+ assertEqI64(module.defined, createI64('0xFAFADADABABA'));
+
+ setJitCompilerOption('wasm.test-mode', 0);
+} else {
+ assertErrorMessage(() => evalText(`(module (global i64 (i64.const 0)))`), TypeError, /NYI/);
+ assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /NYI/);
+}
+
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -215,18 +215,18 @@ assertEq(e[""] instanceof Table, true);
// Table export function identity
var code = textToBinary(`(module
(func $f (result i32) (i32.const 1))
(func $g (result i32) (i32.const 2))
(func $h (result i32) (i32.const 3))
(table (resizable 4))
- (elem 0 $f)
- (elem 2 $g)
+ (elem (i32.const 0) $f)
+ (elem (i32.const 2) $g)
(export "f1" $f)
(export "tbl1" table)
(export "f2" $f)
(export "tbl2" table)
(export "f3" $h)
)`);
var e = new Instance(new Module(code)).exports;
assertEq(String(Object.keys(e)), "f1,tbl1,f2,tbl2,f3");
@@ -262,16 +262,17 @@ var code = textToBinary('(module (import
var tbl = new Table({initial:1});
var e = new Instance(new Module(code), {a:{b:tbl}}).exports;
assertEq(tbl, e.foo);
assertEq(tbl, e.bar);
// Non-existent export errors
assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/);
+assertErrorMessage(() => new Module(textToBinary('(module (export "a" global 0))')), TypeError, /exported global index out of bounds/);
assertErrorMessage(() => new Module(textToBinary('(module (export "a" memory))')), TypeError, /exported memory index out of bounds/);
assertErrorMessage(() => new Module(textToBinary('(module (export "a" table))')), TypeError, /exported table index out of bounds/);
// Default memory/table rules
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (memory 1 1))')), TypeError, /already have default memory/);
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (import "x" "y" (memory 2 2)))')), TypeError, /already have default memory/);
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (table 1 1)) (table 1 1))')), TypeError, /already have default table/);
@@ -304,18 +305,18 @@ assertEq(i8[100], 0xc);
assertEq(i8[101], 0xd);
assertEq(i8[102], 0x0);
// Elem segments on imports
var m = new Module(textToBinary(`
(module
(import "a" "b" (table 10))
- (elem 0 $one $two)
- (elem 3 $three $four)
+ (elem (i32.const 0) $one $two)
+ (elem (i32.const 3) $three $four)
(func $one (result i32) (i32.const 1))
(func $two (result i32) (i32.const 2))
(func $three (result i32) (i32.const 3))
(func $four (result i32) (i32.const 4)))
`));
var tbl = new Table({initial:10});
new Instance(m, {a:{b:tbl}});
assertEq(tbl.get(0)(), 1);
--- a/js/src/jit-test/tests/wasm/table-gc.js
+++ b/js/src/jit-test/tests/wasm/table-gc.js
@@ -16,17 +16,17 @@ const evalText = (str, imports) => new I
var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
// A table should not hold exported functions alive and exported functions
// should not hold their originating table alive. Live exported functions should
// hold instances alive. Nothing should hold the export object alive.
resetFinalizeCount();
-var i = evalText(`(module (table (resizable 2)) (export "tbl" table) (elem 0 $f0) ${callee(0)} ${caller})`);
+var i = evalText(`(module (table (resizable 2)) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
var e = i.exports;
var t = e.tbl;
var f = t.get(0);
assertEq(f(), e.call(0));
assertErrorMessage(() => e.call(1), Error, /bad wasm indirect call/);
assertErrorMessage(() => e.call(2), Error, /out-of-range/);
assertEq(finalizeCount(), 0);
i.edge = makeFinalizeObserver();
@@ -54,17 +54,17 @@ gc();
assertEq(finalizeCount(), 3);
assertEq(f(), 0);
f = null;
gc();
assertEq(finalizeCount(), 5);
// A table should hold the instance of any of its elements alive.
resetFinalizeCount();
-var i = evalText(`(module (table (resizable 1)) (export "tbl" table) (elem 0 $f0) ${callee(0)} ${caller})`);
+var i = evalText(`(module (table (resizable 1)) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
var e = i.exports;
var t = e.tbl;
var f = t.get(0);
i.edge = makeFinalizeObserver();
e.edge = makeFinalizeObserver();
t.edge = makeFinalizeObserver();
f.edge = makeFinalizeObserver();
gc();
--- a/js/src/jit-test/tests/wasm/tables.js
+++ b/js/src/jit-test/tests/wasm/tables.js
@@ -9,53 +9,68 @@ const Table = WebAssembly.Table;
// Explicitly opt into the new binary format for imports and exports until it
// is used by default everywhere.
const textToBinary = str => wasmTextToBinary(str, 'new-format');
const evalText = (str, imports) => new Instance(new Module(textToBinary(str)), imports);
var callee = i => `(func $f${i} (result i32) (i32.const ${i}))`;
-assertErrorMessage(() => new Module(textToBinary(`(module (elem 0 $f0) ${callee(0)})`)), TypeError, /table index out of range/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 0 0))`)), TypeError, /table element out of range/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (func) (elem 0 0 1))`)), TypeError, /table element out of range/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 10 $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 8 $f0 $f0 $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 1 $f0 $f0) (elem 0 $f0) ${callee(0)})`)), TypeError, /must be.*ordered/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 1 $f0 $f0) (elem 2 $f0) ${callee(0)})`)), TypeError, /must be.*disjoint/);
+assertErrorMessage(() => new Module(textToBinary(`(module (elem (i32.const 0) $f0) ${callee(0)})`)), TypeError, /table index out of range/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 0) 0))`)), TypeError, /table element out of range/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (func) (elem (i32.const 0) 0 1))`)), TypeError, /table element out of range/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (func) (elem (f32.const 0) 0) ${callee(0)})`)), TypeError, /type mismatch/);
+
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 10) $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 8) $f0 $f0 $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)), TypeError, /must be.*ordered/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)), TypeError, /must be.*disjoint/);
+
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:10}}), TypeError, /element segment does not fit/);
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), TypeError, /element segment does not fit/);
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (i32.const 1) $f0 $f0) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:0}}), TypeError, /must be.*ordered/);
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}}), TypeError, /must be.*disjoint/);
+
+var tbl = new Table({initial:50});
+assertErrorMessage(() => evalText(`(module
+ (import "globals" "table" (table 10 100))
+ (import "globals" "a" (global i32 immutable))
+ (elem (get_global 0) $f0 $f0)
+ ${callee(0)})
+`, {globals:{a:20, table:tbl}}), Error, /element segment does not fit/);
var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
var call = evalText(`(module (table (resizable 10)) ${callee(0)} ${caller})`).exports.call;
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
-var call = evalText(`(module (table (resizable 10)) (elem 0) ${callee(0)} ${caller})`).exports.call;
+var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0)) ${callee(0)} ${caller})`).exports.call;
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
-var call = evalText(`(module (table (resizable 10)) (elem 0 $f0) ${callee(0)} ${caller})`).exports.call;
+var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`).exports.call;
assertEq(call(0), 0);
assertErrorMessage(() => call(1), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
-var call = evalText(`(module (table (resizable 10)) (elem 1 $f0 $f1) (elem 4 $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
+var call = evalText(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f1) (elem (i32.const 4) $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
assertEq(call(1), 0);
assertEq(call(2), 1);
assertErrorMessage(() => call(3), Error, /bad wasm indirect call/);
assertEq(call(4), 0);
assertEq(call(5), 2);
assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
var tbl = new Table({initial:3});
-var call = evalText(`(module (import "a" "b" (table 2)) (export "tbl" table) (elem 0 $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
+var call = evalText(`(module (import "a" "b" (table 2)) (export "tbl" table) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
assertEq(call(0), 0);
assertEq(call(1), 1);
assertEq(tbl.get(0)(), 0);
assertEq(tbl.get(1)(), 1);
// Call signatures are matched structurally:
var call = evalText(`(module
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2134,16 +2134,18 @@ jit::MakeMRegExpHoistable(MIRGraph& grap
regexp->setMovable();
regexp->setDoNotClone();
// That would be incorrect for global/sticky, because lastIndex
// could be wrong. Therefore setting the lastIndex to 0. That is
// faster than a not movable regexp.
RegExpObject* source = regexp->source();
if (source->sticky() || source->global()) {
+ if (!graph.alloc().ensureBallast())
+ return false;
MConstant* zero = MConstant::New(graph.alloc(), Int32Value(0));
regexp->block()->insertAfter(regexp, zero);
MStoreFixedSlot* lastIndex =
MStoreFixedSlot::New(graph.alloc(), regexp, RegExpObject::lastIndexSlot(), zero);
regexp->block()->insertAfter(zero, lastIndex);
}
}
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4078,23 +4078,30 @@ LIRGenerator::visitHasClass(MHasClass* i
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
MOZ_ASSERT(ins->type() == MIRType::Boolean);
define(new(alloc()) LHasClass(useRegister(ins->object())), ins);
}
void
LIRGenerator::visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins)
{
- define(new(alloc()) LWasmLoadGlobalVar, ins);
+ if (ins->type() == MIRType::Int64)
+ defineInt64(new(alloc()) LWasmLoadGlobalVarI64, ins);
+ else
+ define(new(alloc()) LWasmLoadGlobalVar, ins);
}
void
LIRGenerator::visitWasmStoreGlobalVar(MWasmStoreGlobalVar* ins)
{
- add(new(alloc()) LWasmStoreGlobalVar(useRegisterAtStart(ins->value())), ins);
+ MDefinition* value = ins->value();
+ if (value->type() == MIRType::Int64)
+ add(new(alloc()) LWasmStoreGlobalVarI64(useInt64RegisterAtStart(value)), ins);
+ else
+ add(new(alloc()) LWasmStoreGlobalVar(useRegisterAtStart(value)), ins);
}
void
LIRGenerator::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins)
{
define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index())), ins);
}
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8057,31 +8057,53 @@ class LWasmLoadGlobalVar : public LInstr
{
public:
LIR_HEADER(WasmLoadGlobalVar);
MWasmLoadGlobalVar* mir() const {
return mir_->toWasmLoadGlobalVar();
}
};
+class LWasmLoadGlobalVarI64 : public LInstructionHelper<INT64_PIECES, 0, 0>
+{
+ public:
+ LIR_HEADER(WasmLoadGlobalVarI64);
+ MWasmLoadGlobalVar* mir() const {
+ return mir_->toWasmLoadGlobalVar();
+ }
+};
+
class LWasmStoreGlobalVar : public LInstructionHelper<0, 1, 0>
{
public:
LIR_HEADER(WasmStoreGlobalVar);
explicit LWasmStoreGlobalVar(const LAllocation& value) {
setOperand(0, value);
}
MWasmStoreGlobalVar* mir() const {
return mir_->toWasmStoreGlobalVar();
}
const LAllocation* value() {
return getOperand(0);
}
};
+class LWasmStoreGlobalVarI64 : public LInstructionHelper<0, INT64_PIECES, 0>
+{
+ public:
+ LIR_HEADER(WasmStoreGlobalVarI64);
+ explicit LWasmStoreGlobalVarI64(const LInt64Allocation& value) {
+ setInt64Operand(0, value);
+ }
+ MWasmStoreGlobalVar* mir() const {
+ return mir_->toWasmStoreGlobalVar();
+ }
+ static const uint32_t InputIndex = 0;
+};
+
class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(AsmJSLoadFuncPtr);
explicit LAsmJSLoadFuncPtr(const LAllocation& index) {
setOperand(0, index);
}
const MAsmJSLoadFuncPtr* mir() const {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -385,17 +385,19 @@
_(HasClass) \
_(AsmSelect) \
_(AsmSelectI64) \
_(WasmLoad) \
_(WasmLoadI64) \
_(WasmStore) \
_(WasmBoundsCheck) \
_(WasmLoadGlobalVar) \
+ _(WasmLoadGlobalVarI64) \
_(WasmStoreGlobalVar) \
+ _(WasmStoreGlobalVarI64) \
_(AsmJSLoadHeap) \
_(AsmJSStoreHeap) \
_(AsmJSLoadFuncPtr) \
_(AsmJSLoadFFIFunc) \
_(AsmJSParameter) \
_(AsmJSReturn) \
_(AsmJSVoidReturn) \
_(AsmJSPassStackArg) \
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -1207,16 +1207,25 @@ CodeGeneratorX64::visitWasmLoadGlobalVar
default:
MOZ_CRASH("unexpected type in visitWasmLoadGlobalVar");
}
masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
}
void
+CodeGeneratorX64::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
+{
+ MWasmLoadGlobalVar* mir = ins->mir();
+ MOZ_ASSERT(mir->type() == MIRType::Int64);
+ CodeOffset label = masm.loadRipRelativeInt64(ToRegister(ins->output()));
+ masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
+}
+
+void
CodeGeneratorX64::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
{
MWasmStoreGlobalVar* mir = ins->mir();
MIRType type = mir->value()->type();
MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
CodeOffset label;
@@ -1242,16 +1251,26 @@ CodeGeneratorX64::visitWasmStoreGlobalVa
default:
MOZ_CRASH("unexpected type in visitWasmStoreGlobalVar");
}
masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
}
void
+CodeGeneratorX64::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
+{
+ MWasmStoreGlobalVar* mir = ins->mir();
+ MOZ_ASSERT(mir->value()->type() == MIRType::Int64);
+ Register value = ToRegister(ins->getOperand(LWasmStoreGlobalVarI64::InputIndex));
+ CodeOffset label = masm.storeRipRelativeInt64(value);
+ masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
+}
+
+void
CodeGeneratorX64::visitTruncateDToInt32(LTruncateDToInt32* ins)
{
FloatRegister input = ToFloatRegister(ins->input());
Register output = ToRegister(ins->output());
// On x64, branchTruncateDouble uses vcvttsd2sq. Unlike the x86
// implementation, this should handle most doubles and we can just
// call a stub if it fails.
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -72,16 +72,18 @@ class CodeGeneratorX64 : public CodeGene
void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
void visitWasmLoad(LWasmLoad* ins);
void visitWasmLoadI64(LWasmLoadI64* ins);
void visitWasmStore(LWasmStore* ins);
void visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins);
void visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins);
+ void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
+ void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
void visitAsmSelectI64(LAsmSelectI64* ins);
void visitAsmJSCall(LAsmJSCall* ins);
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -5549,17 +5549,17 @@ static void DebugPaintItem(DrawTarget& a
nsDisplayItem *aItem,
nsDisplayListBuilder* aBuilder)
{
bool snap;
Rect bounds = NSRectToRect(aItem->GetBounds(aBuilder, &snap),
aPresContext->AppUnitsPerDevPixel());
RefPtr<DrawTarget> tempDT =
- aDrawTarget.CreateSimilarDrawTarget(IntSize(bounds.width, bounds.height),
+ aDrawTarget.CreateSimilarDrawTarget(IntSize::Truncate(bounds.width, bounds.height),
SurfaceFormat::B8G8R8A8);
RefPtr<gfxContext> context = gfxContext::CreateOrNull(tempDT);
if (!context) {
// Leave this as crash, it's in the debugging code, we want to know
gfxDevCrash(LogReason::InvalidContext) << "DebugPaintItem context problem " << gfx::hexa(tempDT);
return;
}
context->SetMatrix(gfxMatrix::Translation(-bounds.x, -bounds.y));
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6536,17 +6536,17 @@ ComputeSnappedImageDrawingParameters(gfx
aImage->OptimalImageSizeForDest(snappedDestSize,
imgIContainer::FRAME_CURRENT,
aSamplingFilter, aImageFlags);
gfxSize imageSize(intImageSize.width, intImageSize.height);
// XXX(seth): May be buggy; see bug 1151016.
CSSIntSize svgViewportSize = currentMatrix.IsIdentity()
? CSSIntSize(intImageSize.width, intImageSize.height)
- : CSSIntSize(devPixelDest.width, devPixelDest.height);
+ : CSSIntSize::Truncate(devPixelDest.width, devPixelDest.height);
// Compute the set of pixels that would be sampled by an ideal rendering
gfxPoint subimageTopLeft =
MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft());
gfxPoint subimageBottomRight =
MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight());
gfxRect subimage;
subimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -302,18 +302,18 @@ nsDisplayCanvasBackgroundImage::Paint(ns
RefPtr<DrawTarget> dt =
Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT());
DrawTarget* destDT = dest->GetDrawTarget();
if (dt) {
BlitSurface(destDT, destRect, dt);
return;
}
- dt = destDT->CreateSimilarDrawTarget(IntSize(ceil(destRect.width),
- ceil(destRect.height)),
+ dt = destDT->CreateSimilarDrawTarget(IntSize::Ceil(destRect.width,
+ destRect.height),
SurfaceFormat::B8G8R8A8);
if (dt && dt->IsValid()) {
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
MOZ_ASSERT(ctx); // already checked draw target above
ctx->SetMatrix(ctx->CurrentMatrix().Translate(-destRect.x, -destRect.y));
nsRenderingContext context(ctx);
PaintInternal(aBuilder, &context, bgClipRect, &bgClipRect);
BlitSurface(dest->GetDrawTarget(), destRect, dt);
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -398,17 +398,17 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
// Package up the attributes of this image element which can override the
// attributes of mImageContainer's internal SVG document. The 'width' &
// 'height' values we're passing in here are in CSS units (though they
// come from width/height *attributes* in SVG). They influence the region
// of the SVG image's internal document that is visible, in combination
// with preserveAspectRatio and viewBox.
- SVGImageContext context(CSSIntSize(width, height),
+ SVGImageContext context(CSSIntSize::Truncate(width, height),
Some(imgElem->mPreserveAspectRatio.GetAnimValue()),
1.0, true);
// For the actual draw operation to draw crisply (and at the right size),
// our destination rect needs to be |width|x|height|, *in dev pixels*.
LayoutDeviceSize devPxSize(width, height);
nsRect destRect(nsPoint(),
LayoutDevicePixel::ToAppUnits(devPxSize,
--- a/netwerk/test/unit/test_cache2-29c-concurrent_read_half-interrupted.js
+++ b/netwerk/test/unit/test_cache2-29c-concurrent_read_half-interrupted.js
@@ -55,21 +55,16 @@ function contentHandler(metadata, respon
let len = responseBody.length;
response.setHeader("Content-Range", "0-" + (len - 1) + "/" + len);
}
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function run_test()
{
- if (!newCacheBackEndUsed()) {
- do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
- return;
- }
-
// Static check
do_check_true(responseBody.length > 1024);
do_get_profile();
Services.prefs.setIntPref("browser.cache.disk.max_entry_size", 1);
httpServer = new HttpServer();
--- a/netwerk/test/unit/test_cache2-29d-concurrent_read_half-corrupted-206.js
+++ b/netwerk/test/unit/test_cache2-29d-concurrent_read_half-corrupted-206.js
@@ -60,16 +60,21 @@ function contentHandler(metadata, respon
function run_test()
{
// Static check
do_check_true(responseBody.length > 1024);
do_get_profile();
+ if (!newCacheBackEndUsed()) {
+ do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
+ return;
+ }
+
Services.prefs.setIntPref("browser.cache.disk.max_entry_size", 1);
httpServer = new HttpServer();
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(-1);
var chan1 = make_channel(URL + "/content");
chan1.asyncOpen2(new ChannelListener(firstTimeThrough, null));
--- a/netwerk/test/unit/test_cache2-29e-concurrent_read_half-non-206-response.js
+++ b/netwerk/test/unit/test_cache2-29e-concurrent_read_half-non-206-response.js
@@ -50,26 +50,26 @@ function contentHandler(metadata, respon
response.setHeader("Cache-Control", "max-age=99999");
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Content-Length", "" + responseBody.length);
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function run_test()
{
+ // Static check
+ do_check_true(responseBody.length > 1024);
+
+ do_get_profile();
+
if (!newCacheBackEndUsed()) {
do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
return;
}
- // Static check
- do_check_true(responseBody.length > 1024);
-
- do_get_profile();
-
Services.prefs.setIntPref("browser.cache.disk.max_entry_size", 1);
httpServer = new HttpServer();
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(-1);
var chan1 = make_channel(URL + "/content");
chan1.asyncOpen2(new ChannelListener(firstTimeThrough, null));
--- a/security/sandbox/linux/common/SandboxInfo.cpp
+++ b/security/sandbox/linux/common/SandboxInfo.cpp
@@ -277,19 +277,21 @@ SandboxInfo::SubmitTelemetry()
Telemetry::Accumulate(Telemetry::SANDBOX_HAS_USER_NAMESPACES,
sandboxInfo.Test(SandboxInfo::kHasUserNamespaces));
Telemetry::Accumulate(Telemetry::SANDBOX_CONTENT_ENABLED,
sandboxInfo.Test(SandboxInfo::kEnabledForContent));
Telemetry::Accumulate(Telemetry::SANDBOX_MEDIA_ENABLED,
sandboxInfo.Test(SandboxInfo::kEnabledForMedia));
}
+#ifdef MOZ_CRASHREPORTER
void
SandboxInfo::AnnotateCrashReport() const
{
nsAutoCString flagsString;
flagsString.AppendInt(mFlags);
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
}
+#endif
} // namespace mozilla
--- a/security/sandbox/linux/common/SandboxInfo.h
+++ b/security/sandbox/linux/common/SandboxInfo.h
@@ -52,17 +52,19 @@ public:
}
// Returns true if SetMediaPluginSandbox may be called.
bool CanSandboxMedia() const
{
return !Test(kEnabledForMedia) || Test(kHasSeccompBPF);
}
+#ifdef MOZ_CRASHREPORTER
MOZ_EXPORT void AnnotateCrashReport() const;
+#endif
static void SubmitTelemetry();
// For bug 1222500 or anything else like it: On desktop, this is
// called in the parent process at a point when it should still be
// single-threaded, to check that the SandboxEarlyInit() call in a
// child process is early enough to be single-threaded. If not,
// kUnexpectedThreads is set and affected flags (user namespaces;
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -16,18 +16,18 @@ namespace mozilla {
using namespace dom;
already_AddRefed<Touch> SingleTouchData::ToNewDOMTouch() const
{
MOZ_ASSERT(NS_IsMainThread(),
"Can only create dom::Touch instances on main thread");
RefPtr<Touch> touch = new Touch(mIdentifier,
- LayoutDeviceIntPoint(mScreenPoint.x, mScreenPoint.y),
- LayoutDeviceIntPoint(mRadius.width, mRadius.height),
+ LayoutDeviceIntPoint::Truncate(mScreenPoint.x, mScreenPoint.y),
+ LayoutDeviceIntPoint::Truncate(mRadius.width, mRadius.height),
mRotationAngle,
mForce);
return touch.forget();
}
MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent)
: InputData(MOUSE_INPUT, aMouseEvent.mTime, aMouseEvent.mTimeStamp,
aMouseEvent.mModifiers)
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -502,20 +502,20 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWid
const LayoutDeviceIntPoint& offset = widget->WidgetToScreenOffset();
event.mTouches.SetCapacity(endIndex - startIndex);
for (int i = startIndex; i < endIndex; i++) {
// In this code branch, we are dispatching this event directly
// into Gecko (as opposed to going through the AsyncPanZoomController),
// and the Points() array has points in CSS pixels, which we need
// to convert.
CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
- LayoutDeviceIntPoint pt(
+ auto pt = LayoutDeviceIntPoint::Truncate(
(Points()[i].x * scale.scale) - offset.x,
(Points()[i].y * scale.scale) - offset.y);
- LayoutDeviceIntPoint radius(
+ auto radius = LayoutDeviceIntPoint::Truncate(
PointRadii()[i].x * scale.scale,
PointRadii()[i].y * scale.scale);
RefPtr<Touch> t = new Touch(PointIndicies()[i],
pt,
radius,
Orientations()[i],
Pressures()[i]);
event.mTouches.AppendElement(t);
@@ -620,18 +620,18 @@ AndroidGeckoEvent::MakeMouseEvent(nsIWid
event.mTime = Time();
// We are dispatching this event directly into Gecko (as opposed to going
// through the AsyncPanZoomController), and the Points() array has points
// in CSS pixels, which we need to convert to LayoutDevice pixels.
const LayoutDeviceIntPoint& offset = widget->WidgetToScreenOffset();
CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
event.mRefPoint =
- LayoutDeviceIntPoint((Points()[0].x * scale.scale) - offset.x,
- (Points()[0].y * scale.scale) - offset.y);
+ LayoutDeviceIntPoint::Truncate((Points()[0].x * scale.scale) - offset.x,
+ (Points()[0].y * scale.scale) - offset.y);
return event;
}
Modifiers
AndroidGeckoEvent::DOMModifiers() const
{
Modifiers result = 0;
if (mMetaState & AMETA_ALT_MASK) {
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1615,18 +1615,19 @@ nsWindow::Resize(double aX,
bool needSizeDispatch = aWidth != mBounds.width || aHeight != mBounds.height;
mBounds.x = NSToIntRound(aX);
mBounds.y = NSToIntRound(aY);
mBounds.width = NSToIntRound(aWidth);
mBounds.height = NSToIntRound(aHeight);
- if (needSizeDispatch)
- OnSizeChanged(gfx::IntSize(aWidth, aHeight));
+ if (needSizeDispatch) {
+ OnSizeChanged(gfx::IntSize::Truncate(aWidth, aHeight));
+ }
// Should we skip honoring aRepaint here?
if (aRepaint && FindTopLevel() == nsWindow::TopWindow())
RedrawAll();
nsIWidgetListener* listener = GetWidgetListener();
if (mAwaitingFullScreen && listener) {
listener->FullscreenChanged(mIsFullScreen);
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -3741,17 +3741,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
// scales one user space unit to one Cocoa point, which can consist of
// multiple dev pixels. But Gecko expects its supplied context to be scaled
// to device pixels, so we need to reverse the scaling.
double scale = mGeckoChild->BackingScaleFactor();
CGContextSaveGState(aContext);
CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale);
NSSize viewSize = [self bounds].size;
- nsIntSize backingSize(viewSize.width * scale, viewSize.height * scale);
+ gfx::IntSize backingSize = gfx::IntSize::Truncate(viewSize.width * scale, viewSize.height * scale);
LayoutDeviceIntRegion region = [self nativeDirtyRegionWithBoundingRect:aRect];
bool painted = mGeckoChild->PaintWindowInContext(aContext, region, backingSize);
// Undo the scale transform so that from now on the context is in
// CocoaPoints again.
CGContextRestoreGState(aContext);
@@ -4121,17 +4121,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
break;
}
}
if (shouldRollup) {
if ([theEvent type] == NSLeftMouseDown) {
NSPoint point = [NSEvent mouseLocation];
FlipCocoaScreenCoordinate(point);
- nsIntPoint pos(point.x, point.y);
+ gfx::IntPoint pos = gfx::IntPoint::Truncate(point.x, point.y);
consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, true, &pos, nullptr);
}
else {
consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr);
}
}
}
}
@@ -5765,17 +5765,17 @@ PanGestureTypeForEvent(NSEvent* aEvent)
NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
}
if (mDragService) {
// set the dragend point from the current mouse location
nsDragService* dragService = static_cast<nsDragService *>(mDragService);
NSPoint pnt = [NSEvent mouseLocation];
FlipCocoaScreenCoordinate(pnt);
- dragService->SetDragEndPoint(nsIntPoint(NSToIntRound(pnt.x), NSToIntRound(pnt.y)));
+ dragService->SetDragEndPoint(gfx::IntPoint::Round(pnt.x, pnt.y));
// XXX: dropEffect should be updated per |operation|.
// As things stand though, |operation| isn't well handled within "our"
// events, that is, when the drop happens within the window: it is set
// either to NSDragOperationGeneric or to NSDragOperationNone.
// For that reason, it's not yet possible to override dropEffect per the
// given OS value, and it's also unclear what's the correct dropEffect
// value for NSDragOperationGeneric that is passed by other applications.
--- a/widget/cocoa/nsCocoaUtils.mm
+++ b/widget/cocoa/nsCocoaUtils.mm
@@ -472,17 +472,17 @@ nsresult nsCocoaUtils::CreateNSImageFrom
{
RefPtr<SourceSurface> surface;
int32_t width = 0, height = 0;
aImage->GetWidth(&width);
aImage->GetHeight(&height);
// Render a vector image at the correct resolution on a retina display
if (aImage->GetType() == imgIContainer::TYPE_VECTOR && scaleFactor != 1.0f) {
- IntSize scaledSize(ceil(width * scaleFactor), ceil(height * scaleFactor));
+ IntSize scaledSize = IntSize::Ceil(width * scaleFactor, height * scaleFactor);
RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(scaledSize, SurfaceFormat::B8G8R8A8);
if (!drawTarget || !drawTarget->IsValid()) {
NS_ERROR("Failed to create valid DrawTarget");
return NS_ERROR_FAILURE;
}
--- a/widget/cocoa/nsDeviceContextSpecX.mm
+++ b/widget/cocoa/nsDeviceContextSpecX.mm
@@ -141,17 +141,17 @@ void nsDeviceContextSpecX::GetPaperRect(
already_AddRefed<PrintTarget> nsDeviceContextSpecX::MakePrintTarget()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
double top, left, bottom, right;
GetPaperRect(&top, &left, &bottom, &right);
const double width = right - left;
const double height = bottom - top;
- IntSize size(floor(width), floor(height));
+ IntSize size = IntSize::Floor(width, height);
CGContextRef context;
::PMSessionGetCGGraphicsContext(mPrintSession, &context);
if (context) {
// Initially, origin is at bottom-left corner of the paper.
// Here, we translate it to top-left corner of the paper.
CGContextTranslateCTM(context, 0, height);
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -156,17 +156,17 @@ already_AddRefed<PrintTarget> nsDeviceCo
if (mIsPPreview) {
// There is nothing to detect on Print Preview, use PS.
format = nsIPrintSettings::kOutputFormatPS;
} else {
return nullptr;
}
}
- IntSize size(width, height);
+ IntSize size = IntSize::Truncate(width, height);
if (format == nsIPrintSettings::kOutputFormatPDF) {
return PrintTargetPDF::CreateOrNull(stream, size);
}
int32_t orientation;
mPrintSettings->GetOrientation(&orientation);
return PrintTargetPS::CreateOrNull(stream,
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -232,18 +232,18 @@ OnSourceGrabEventAfter(GtkWidget *widget
gdk_event_free(sMotionEvent);
}
sMotionEvent = gdk_event_copy(event);
// Update the cursor position. The last of these recorded gets used for
// the eDragEnd event.
nsDragService *dragService = static_cast<nsDragService*>(user_data);
gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
- LayoutDeviceIntPoint p(floor(event->motion.x_root * scale + 0.5),
- floor(event->motion.y_root * scale + 0.5));
+ auto p = LayoutDeviceIntPoint::Round(event->motion.x_root * scale,
+ event->motion.y_root * scale);
dragService->SetDragEndPoint(p);
} else if (sMotionEvent && (event->type == GDK_KEY_PRESS ||
event->type == GDK_KEY_RELEASE)) {
// Update modifier state from key events.
sMotionEvent->motion.state = event->key.state;
} else {
return;
}
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -5205,17 +5205,17 @@ nsWindow::CheckForRollup(gdouble aMouseX
}
break;
}
} // foreach parent menu widget
} // if rollup listener knows about menus
// if we've determined that we should still rollup, do it.
bool usePoint = !aIsWheel && !aAlwaysRollup;
- nsIntPoint point(aMouseX, aMouseY);
+ IntPoint point = IntPoint::Truncate(aMouseX, aMouseY);
if (rollup && rollupListener->Rollup(popupsToRollup, true, usePoint ? &point : nullptr, nullptr)) {
retVal = true;
}
}
return retVal;
}
/* static */
@@ -6806,17 +6806,17 @@ int
nsWindow::GdkCoordToDevicePixels(gint coord) {
return coord * GdkScaleFactor();
}
LayoutDeviceIntPoint
nsWindow::GdkEventCoordsToDevicePixels(gdouble x, gdouble y)
{
gint scale = GdkScaleFactor();
- return LayoutDeviceIntPoint(floor(x * scale + 0.5), floor(y * scale + 0.5));
+ return LayoutDeviceIntPoint::Round(x * scale, y * scale);
}
LayoutDeviceIntPoint
nsWindow::GdkPointToDevicePixels(GdkPoint point) {
gint scale = GdkScaleFactor();
return LayoutDeviceIntPoint(point.x * scale,
point.y * scale);
}
--- a/widget/nsDeviceContextSpecProxy.cpp
+++ b/widget/nsDeviceContextSpecProxy.cpp
@@ -76,17 +76,17 @@ nsDeviceContextSpecProxy::MakePrintTarge
return nullptr;
}
// convert twips to points
width /= TWIPS_PER_POINT_FLOAT;
height /= TWIPS_PER_POINT_FLOAT;
RefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->
- CreateOffscreenSurface(mozilla::gfx::IntSize(width, height),
+ CreateOffscreenSurface(mozilla::gfx::IntSize::Truncate(width, height),
mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32);
if (!surface) {
return nullptr;
}
// The type of PrintTarget that we return here shouldn't really matter since
// our implementation of GetDrawEventRecorder returns an object, which means
// the DrawTarget returned by the PrintTarget will be a DrawTargetRecording.
--- a/widget/windows/nsDeviceContextSpecWin.cpp
+++ b/widget/windows/nsDeviceContextSpecWin.cpp
@@ -247,17 +247,17 @@ already_AddRefed<PrintTarget> nsDeviceCo
}
nsCOMPtr<nsIFileOutputStream> stream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
rv = stream->Init(file, -1, -1, 0);
if (NS_FAILED(rv)) {
return nullptr;
}
- return PrintTargetPDF::CreateOrNull(stream, IntSize(width, height));
+ return PrintTargetPDF::CreateOrNull(stream, IntSize::Truncate(width, height));
}
if (mDevMode) {
NS_WARN_IF_FALSE(mDriverName, "No driver!");
HDC dc = ::CreateDCW(mDriverName, mDeviceName, nullptr, mDevMode);
if (!dc) {
gfxCriticalError(gfxCriticalError::DefaultOptions(false))
<< "Failed to create device context in GetSurfaceForPrinter";