mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 09:05:45 +00:00
Merge m-c to fx-team, a=merge
This commit is contained in:
commit
f050631553
@ -10784,7 +10784,8 @@ nsDocShell::DoURILoad(nsIURI* aURI,
|
||||
// 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.
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -22,6 +22,10 @@ support-files =
|
||||
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
|
||||
@ -48,3 +52,4 @@ skip-if = (buildapp == 'b2g' && debug) || (toolkit == 'android') || (debug && e1
|
||||
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]
|
||||
|
@ -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>
|
@ -701,8 +701,8 @@ 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());
|
||||
}
|
||||
@ -741,8 +741,8 @@ 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());
|
||||
}
|
||||
}
|
||||
|
@ -1332,12 +1332,17 @@ Navigator::SendBeacon(const nsAString& aUrl,
|
||||
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
|
||||
|
@ -2808,7 +2808,7 @@ CheckLeafLayers(Layer* aLayer, const nsIntPoint& aOffset, nsIntRegion* aCoveredR
|
||||
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) {
|
||||
|
@ -7724,8 +7724,8 @@ 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());
|
||||
}
|
||||
|
||||
@ -7733,8 +7733,8 @@ 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());
|
||||
}
|
||||
|
||||
@ -7803,8 +7803,8 @@ nsGlobalWindow::ScrollBy(double aXScrollDif, double aYScrollDif)
|
||||
|
||||
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.
|
||||
|
@ -133,46 +133,58 @@ nsLocation::CheckURL(nsIURI* aURI, nsIDocShellLoadInfo** aLoadInfo)
|
||||
// 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
|
||||
|
@ -4820,8 +4820,8 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas(
|
||||
|
||||
// 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
|
||||
@ -5045,7 +5045,7 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX,
|
||||
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);
|
||||
|
@ -60,10 +60,10 @@ nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
|
||||
// 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;
|
||||
|
@ -181,7 +181,7 @@ AttachToContainerAsSurfaceTexture(ImageContainer* container,
|
||||
|
||||
RefPtr<Image> img = new SurfaceTextureImage(
|
||||
surfTex,
|
||||
gfx::IntSize(rect.width, rect.height),
|
||||
gfx::IntSize::Truncate(rect.width, rect.height),
|
||||
instance->OriginPos());
|
||||
*out_image = img;
|
||||
}
|
||||
@ -222,7 +222,7 @@ nsPluginInstanceOwner::GetImageContainer()
|
||||
// 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();
|
||||
|
||||
@ -1584,7 +1584,7 @@ nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInf
|
||||
|
||||
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);
|
||||
|
||||
|
@ -147,7 +147,7 @@ SVGFETurbulenceElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
|
||||
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);
|
||||
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* TestSever customized specifically for the needs of:
|
||||
* Bug 1111834 - sendBeacon() should not follow 30x redirect after preflight
|
||||
*
|
||||
* 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
|
||||
* 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") {
|
||||
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]
|
||||
if (request.queryString === "beacon") {
|
||||
setState("redirectState", "green");
|
||||
// [1]
|
||||
if (request.method == "OPTIONS") {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
return;
|
||||
}
|
||||
// [Sequence 2]
|
||||
var newLocation =
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs?redirected";
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", newLocation, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// [Sequence 3]
|
||||
setState("redirectState", "red");
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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)
|
||||
@ -26,9 +26,16 @@ function handleRequest(request, response)
|
||||
|
||||
// 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) {
|
||||
|
47
dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
Normal file
47
dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* TestSever customized specifically for the needs of:
|
||||
* Bug 1280692 - sendBeacon() should follow 30x redirect
|
||||
*
|
||||
* Here is a sequence of the test:
|
||||
* [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 3]
|
||||
if (request.queryString === "verifyRedirectDidSucceed") {
|
||||
var redirectState = getState("redirectState");
|
||||
response.write(redirectState);
|
||||
return;
|
||||
}
|
||||
|
||||
// [Sequence 1]
|
||||
if (request.queryString === "beacon") {
|
||||
setState("redirectState", "reset");
|
||||
var newLocation =
|
||||
"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 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");
|
||||
}
|
@ -4,13 +4,11 @@ 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]
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!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" />
|
||||
@ -17,18 +17,21 @@
|
||||
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.
|
||||
*/
|
||||
|
||||
@ -38,7 +41,7 @@ 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() {
|
||||
|
@ -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>
|
@ -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>
|
@ -1,7 +1,7 @@
|
||||
<!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" />
|
||||
@ -15,13 +15,13 @@
|
||||
<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);
|
||||
|
||||
@ -30,9 +30,9 @@ 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() {
|
||||
@ -46,7 +46,7 @@ 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);
|
||||
}
|
@ -339,7 +339,7 @@ DrawTargetD2D1::MaskSurface(const Pattern &aSource,
|
||||
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));
|
||||
|
||||
|
@ -538,7 +538,7 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
SkPaint shadowPaint;
|
||||
shadowPaint.setXfermodeMode(GfxOpToSkiaOp(aOperator));
|
||||
|
||||
IntPoint shadowDest = RoundedToInt(aDest + aOffset);
|
||||
auto shadowDest = IntPoint::Round(aDest + aOffset);
|
||||
|
||||
SkBitmap blurMask;
|
||||
if (!UsingSkiaGPU() &&
|
||||
@ -569,7 +569,7 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
139
gfx/2d/Point.h
139
gfx/2d/Point.h
@ -33,6 +33,36 @@ 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> >,
|
||||
@ -40,16 +70,33 @@ struct IntPointTyped :
|
||||
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.
|
||||
@ -99,14 +146,12 @@ 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>
|
||||
@ -134,6 +179,34 @@ struct Point3DTyped :
|
||||
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> > {
|
||||
@ -170,10 +243,32 @@ struct IntSizeTyped :
|
||||
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.
|
||||
@ -222,6 +317,26 @@ IntSizeTyped<units> RoundedToInt(const SizeTyped<units>& aSize) {
|
||||
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
|
||||
|
||||
|
@ -22,7 +22,7 @@ SharedSurface_IOSurface::Create(const RefPtr<MacIOSurface>& ioSurf,
|
||||
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) );
|
||||
@ -214,8 +214,8 @@ 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) );
|
||||
|
@ -631,7 +631,7 @@ Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
|
||||
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);
|
||||
@ -663,8 +663,7 @@ Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
|
||||
|
||||
// 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()
|
||||
@ -722,9 +721,9 @@ Layer::SnapTransform(const Matrix4x4& aTransform,
|
||||
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);
|
||||
@ -1028,7 +1027,7 @@ Layer::TransformRectToRenderTarget(const LayerIntRect& aRect)
|
||||
|
||||
bool
|
||||
Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
|
||||
nsIntPoint* aLayerOffset)
|
||||
IntPoint* aLayerOffset)
|
||||
{
|
||||
MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
|
||||
|
||||
@ -1046,7 +1045,7 @@ Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
|
||||
}
|
||||
|
||||
// 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|.
|
||||
@ -1073,7 +1072,7 @@ Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
|
||||
|
||||
// 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);
|
||||
@ -1092,7 +1091,7 @@ Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
|
||||
offset += currentLayerOffset;
|
||||
}
|
||||
|
||||
*aLayerOffset = nsIntPoint(offset.x, offset.y);
|
||||
*aLayerOffset = IntPoint(offset.x, offset.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1387,7 +1387,7 @@ public:
|
||||
* 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.
|
||||
|
@ -36,7 +36,7 @@ CreateSourceSurfaceFromLockedMacIOSurface(MacIOSurface* aSurface)
|
||||
: 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;
|
||||
}
|
||||
@ -75,14 +75,14 @@ CreateSourceSurfaceFromLockedMacIOSurface(MacIOSurface* aSurface)
|
||||
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;
|
||||
@ -128,14 +128,14 @@ CreateSourceSurfaceFromLockedMacIOSurface(MacIOSurface* aSurface)
|
||||
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();
|
||||
|
||||
|
@ -25,7 +25,8 @@ public:
|
||||
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;
|
||||
|
@ -158,7 +158,7 @@ public:
|
||||
|
||||
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(); }
|
||||
|
||||
|
@ -267,7 +267,7 @@ HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
|
||||
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)) {
|
||||
|
@ -430,7 +430,7 @@ APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
|
||||
{
|
||||
LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
|
||||
point = ApplyCallbackTransform(point / aScale, aGuid) * aScale;
|
||||
return gfx::RoundedToInt(point);
|
||||
return LayoutDeviceIntPoint::Round(point);
|
||||
}
|
||||
|
||||
void
|
||||
@ -471,7 +471,7 @@ APZCCallbackHelper::DispatchSynthesizedMouseEvent(EventMessage aMsg,
|
||||
|
||||
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;
|
||||
|
@ -256,7 +256,7 @@ APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
|
||||
// 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);
|
||||
|
@ -360,9 +360,9 @@ DrawSurfaceWithTextureCoords(DrawTarget *aDest,
|
||||
// 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);
|
||||
@ -626,7 +626,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect,
|
||||
|
||||
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 &&
|
||||
|
@ -936,7 +936,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
|
||||
|
||||
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 &&
|
||||
|
@ -849,7 +849,12 @@ 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) {
|
||||
|
@ -360,10 +360,11 @@ DeallocateTextureClient(TextureDeallocParams params)
|
||||
|
||||
void TextureClient::Destroy(bool aForceSync)
|
||||
{
|
||||
if (mActor) {
|
||||
if (mActor && !mIsLocked) {
|
||||
mActor->Lock();
|
||||
}
|
||||
|
||||
mBorrowedDrawTarget = nullptr;
|
||||
mReadLock = nullptr;
|
||||
|
||||
CancelWaitFenceHandleOnImageBridge();
|
||||
@ -519,7 +520,9 @@ TextureClient::Unlock()
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
mData->Unlock();
|
||||
if (mData) {
|
||||
mData->Unlock();
|
||||
}
|
||||
mIsLocked = false;
|
||||
mOpenMode = OpenMode::OPEN_NONE;
|
||||
|
||||
|
@ -726,7 +726,7 @@ CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
|
||||
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();
|
||||
|
||||
|
@ -251,13 +251,13 @@ LayerManagerComposite::PostProcessLayers(Layer* aLayer,
|
||||
// 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);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ PrintTargetWindows::CreateOrNull(HDC aDC)
|
||||
(::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;
|
||||
|
@ -520,8 +520,8 @@ ClippedImage::OptimalImageSizeForDest(const gfxSize& aDest,
|
||||
|
||||
// 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);
|
||||
@ -537,8 +537,8 @@ ClippedImage::OptimalImageSizeForDest(const gfxSize& aDest,
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -1823,7 +1823,7 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
||||
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)) {
|
||||
|
@ -1302,7 +1302,7 @@ VectorImage::OptimalImageSizeForDest(const gfxSize& aDest,
|
||||
"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>
|
||||
|
@ -111,7 +111,6 @@ class AsmJSGlobal
|
||||
Which which_;
|
||||
union {
|
||||
struct {
|
||||
uint32_t globalDataOffset_;
|
||||
VarInitKind initKind_;
|
||||
union {
|
||||
ValType importType_;
|
||||
@ -150,10 +149,6 @@ class AsmJSGlobal
|
||||
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_;
|
||||
@ -1447,7 +1442,6 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
}
|
||||
unsigned varOrConstIndex() const {
|
||||
MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
|
||||
MOZ_ASSERT(u.varOrConst.index_ != -1u);
|
||||
return u.varOrConst.index_;
|
||||
}
|
||||
bool isConst() const {
|
||||
@ -1851,7 +1845,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
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;
|
||||
@ -1868,7 +1862,6 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
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) {
|
||||
@ -1880,7 +1873,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
|
||||
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;
|
||||
@ -1895,7 +1888,6 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
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) {
|
||||
@ -3905,7 +3897,7 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
|
||||
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:
|
||||
@ -4197,7 +4189,7 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type
|
||||
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;
|
||||
@ -7817,25 +7809,9 @@ CheckBuffer(JSContext* cx, const AsmJSMetadata& metadata, HandleValue bufferVal,
|
||||
}
|
||||
|
||||
static bool
|
||||
TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
|
||||
MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
|
||||
GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
|
||||
HandleValue importVal, MutableHandle<FunctionVector> funcImports, ValVector* valImports)
|
||||
{
|
||||
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);
|
||||
Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
|
||||
if (!ffis.resize(metadata.numFFIs))
|
||||
return false;
|
||||
@ -7843,12 +7819,10 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata
|
||||
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;
|
||||
}
|
||||
@ -7884,14 +7858,40 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata
|
||||
}
|
||||
}
|
||||
|
||||
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
|
||||
for (const AsmJSImport& import : metadata.asmJSImports) {
|
||||
if (!funcs.append(ffis[import.ffiIndex()]))
|
||||
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));
|
||||
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);
|
||||
@ -7900,16 +7900,6 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -195,11 +195,13 @@ enum class AstExprKind
|
||||
ComparisonOperator,
|
||||
Const,
|
||||
ConversionOperator,
|
||||
GetGlobal,
|
||||
GetLocal,
|
||||
If,
|
||||
Load,
|
||||
Nop,
|
||||
Return,
|
||||
SetGlobal,
|
||||
SetLocal,
|
||||
Store,
|
||||
TernaryOperator,
|
||||
@ -290,6 +292,41 @@ class AstSetLocal : public AstExpr
|
||||
}
|
||||
};
|
||||
|
||||
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_;
|
||||
@ -527,14 +564,42 @@ class AstResizable
|
||||
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)
|
||||
@ -543,30 +608,48 @@ class AstImport : public AstNode
|
||||
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
|
||||
@ -586,14 +669,15 @@ 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_; }
|
||||
};
|
||||
@ -636,6 +720,7 @@ class AstModule : public AstNode
|
||||
FuncVector funcs_;
|
||||
AstDataSegmentVector dataSegments_;
|
||||
AstElemSegmentVector elemSegments_;
|
||||
AstGlobalVector globals_;
|
||||
|
||||
public:
|
||||
explicit AstModule(LifoAlloc& lifo)
|
||||
@ -646,7 +731,8 @@ class AstModule : public AstNode
|
||||
exports_(lifo),
|
||||
funcs_(lifo),
|
||||
dataSegments_(lifo),
|
||||
elemSegments_(lifo)
|
||||
elemSegments_(lifo),
|
||||
globals_(lifo)
|
||||
{}
|
||||
bool init() {
|
||||
return sigMap_.init();
|
||||
@ -727,18 +813,24 @@ class AstModule : public AstNode
|
||||
const FuncVector& funcs() const {
|
||||
return funcs_;
|
||||
}
|
||||
const ImportVector& imports() const {
|
||||
return imports_;
|
||||
}
|
||||
bool append(AstImport* imp) {
|
||||
return imports_.append(imp);
|
||||
}
|
||||
const ImportVector& imports() const {
|
||||
return imports_;
|
||||
}
|
||||
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
|
||||
|
@ -5468,28 +5468,49 @@ BaseCompiler::emitGetGlobal()
|
||||
|
||||
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;
|
||||
}
|
||||
@ -5513,28 +5534,28 @@ BaseCompiler::emitSetGlobal()
|
||||
|
||||
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;
|
||||
}
|
||||
@ -6036,9 +6057,9 @@ BaseCompiler::emitBody()
|
||||
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
|
||||
|
@ -28,6 +28,7 @@ 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";
|
||||
@ -70,7 +71,8 @@ enum class DefinitionKind
|
||||
{
|
||||
Function = 0x00,
|
||||
Table = 0x01,
|
||||
Memory = 0x02
|
||||
Memory = 0x02,
|
||||
Global = 0x03
|
||||
};
|
||||
|
||||
enum class ResizableFlags
|
||||
@ -80,6 +82,12 @@ enum class ResizableFlags
|
||||
AllowedMask = 0x3
|
||||
};
|
||||
|
||||
enum class GlobalFlags
|
||||
{
|
||||
IsMutable = 0x1,
|
||||
AllowedMask = 0x1
|
||||
};
|
||||
|
||||
enum class Expr
|
||||
{
|
||||
// Control flow operators
|
||||
@ -271,13 +279,15 @@ enum class Expr
|
||||
// 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,
|
||||
|
@ -329,10 +329,10 @@ wasm::Classify(Expr expr)
|
||||
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;
|
||||
|
@ -1211,7 +1211,7 @@ ExprIter<Policy>::readGetGlobal(const GlobalDescVector& globals, uint32_t* id)
|
||||
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)
|
||||
@ -1233,7 +1233,10 @@ ExprIter<Policy>::readSetGlobal(const GlobalDescVector& globals, uint32_t* id, V
|
||||
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)
|
||||
|
@ -1391,6 +1391,71 @@ AstDecodeMemorySection(AstDecodeContext& c)
|
||||
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_)
|
||||
{
|
||||
@ -1405,7 +1470,8 @@ AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
|
||||
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;
|
||||
|
||||
@ -1647,6 +1713,9 @@ wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length,
|
||||
if (!AstDecodeMemorySection(c))
|
||||
return false;
|
||||
|
||||
if (!AstDecodeGlobalSection(c))
|
||||
return false;
|
||||
|
||||
if (!AstDecodeExportSection(c))
|
||||
return false;
|
||||
|
||||
|
@ -1565,9 +1565,9 @@ PrintExport(WasmPrintContext& c, AstExport& export_, const AstModule::FuncVector
|
||||
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()))
|
||||
|
@ -1174,9 +1174,9 @@ RenderExport(WasmRenderContext& c, AstExport& export_, const AstModule::FuncVect
|
||||
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()))
|
||||
|
@ -417,6 +417,7 @@ Metadata::serializedSize() const
|
||||
SerializedVectorSize(funcImports) +
|
||||
SerializedVectorSize(funcExports) +
|
||||
SerializedVectorSize(sigIds) +
|
||||
SerializedPodVectorSize(globals) +
|
||||
SerializedPodVectorSize(tables) +
|
||||
SerializedPodVectorSize(memoryAccesses) +
|
||||
SerializedPodVectorSize(boundsChecks) +
|
||||
@ -435,6 +436,7 @@ Metadata::serialize(uint8_t* cursor) const
|
||||
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);
|
||||
@ -454,6 +456,7 @@ Metadata::deserialize(const uint8_t* cursor)
|
||||
(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)) &&
|
||||
@ -472,6 +475,7 @@ 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) +
|
||||
|
@ -424,7 +424,6 @@ typedef Vector<char16_t, 64> TwoByteName;
|
||||
// Metadata is built incrementally by ModuleGenerator and then shared immutably
|
||||
// between modules.
|
||||
|
||||
|
||||
class MetadataCacheablePod
|
||||
{
|
||||
static const uint32_t NO_START_FUNCTION = UINT32_MAX;
|
||||
@ -467,6 +466,7 @@ struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
|
||||
FuncImportVector funcImports;
|
||||
FuncExportVector funcExports;
|
||||
SigWithIdVector sigIds;
|
||||
GlobalDescVector globals;
|
||||
TableDescVector tables;
|
||||
MemoryAccessVector memoryAccesses;
|
||||
BoundsCheckVector boundsChecks;
|
||||
|
@ -236,6 +236,10 @@ DecodeExpr(FunctionDecoder& f)
|
||||
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:
|
||||
@ -746,6 +750,48 @@ DecodeResizableTable(Decoder& d, ModuleGeneratorData* init)
|
||||
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)
|
||||
{
|
||||
@ -810,6 +856,17 @@ DecodeImport(Decoder& d, bool newFormat, ModuleGeneratorData* init, ImportVector
|
||||
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");
|
||||
}
|
||||
@ -946,6 +1003,105 @@ DecodeMemorySection(Decoder& d, bool newFormat, ModuleGeneratorData* init, bool*
|
||||
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
|
||||
@ -1032,6 +1188,20 @@ DecodeExport(Decoder& d, bool newFormat, ModuleGenerator& mg, CStringSet* dupSet
|
||||
|
||||
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");
|
||||
}
|
||||
@ -1133,7 +1303,6 @@ DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
|
||||
static bool
|
||||
DecodeStartSection(Decoder& d, ModuleGenerator& mg)
|
||||
{
|
||||
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(StartSectionId, §ionStart, §ionSize))
|
||||
return Fail(d, "failed to start section");
|
||||
@ -1207,7 +1376,7 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
|
||||
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;
|
||||
@ -1231,17 +1400,10 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
|
||||
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;
|
||||
@ -1249,8 +1411,11 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
|
||||
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;
|
||||
@ -1262,7 +1427,8 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
|
||||
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;
|
||||
@ -1459,6 +1625,9 @@ wasm::Compile(const ShareableBytes& bytecode, CompileArgs&& args, UniqueChars* e
|
||||
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;
|
||||
|
@ -172,6 +172,13 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, CompileArgs&& args,
|
||||
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);
|
||||
@ -590,11 +597,11 @@ ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* g
|
||||
}
|
||||
|
||||
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;
|
||||
@ -621,8 +628,22 @@ ModuleGenerator::allocateGlobal(ValType type, bool isConst, uint32_t* index)
|
||||
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
|
||||
@ -713,7 +734,7 @@ ModuleGenerator::funcImport(uint32_t funcImportIndex) const
|
||||
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);
|
||||
}
|
||||
|
||||
@ -731,6 +752,12 @@ 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)
|
||||
{
|
||||
@ -869,8 +896,6 @@ ModuleGenerator::finishFuncDefs()
|
||||
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))
|
||||
@ -915,7 +940,7 @@ ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncInd
|
||||
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
|
||||
@ -970,6 +995,7 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
|
||||
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.
|
||||
|
@ -136,6 +136,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
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);
|
||||
@ -165,8 +166,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
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;
|
||||
@ -176,6 +176,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
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();
|
||||
@ -201,6 +202,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
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
|
||||
|
@ -203,51 +203,6 @@ Instance::toggleProfiling(JSContext* cx)
|
||||
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)
|
||||
@ -423,7 +378,8 @@ Instance::Instance(JSContext* cx,
|
||||
const ShareableBytes* maybeBytecode,
|
||||
HandleWasmMemoryObject memory,
|
||||
SharedTableVector&& tables,
|
||||
Handle<FunctionVector> funcImports)
|
||||
Handle<FunctionVector> funcImports,
|
||||
const ValVector& globalImports)
|
||||
: codeSegment_(Move(codeSegment)),
|
||||
metadata_(&metadata),
|
||||
maybeBytecode_(maybeBytecode),
|
||||
@ -442,6 +398,40 @@ Instance::Instance(JSContext* cx,
|
||||
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();
|
||||
|
||||
|
@ -89,7 +89,8 @@ class Instance
|
||||
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);
|
||||
|
@ -1848,8 +1848,46 @@ EmitGetGlobal(FunctionCompiler& f)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1862,7 +1900,9 @@ EmitSetGlobal(FunctionCompiler& f)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2829,9 +2869,9 @@ EmitExpr(FunctionCompiler& f)
|
||||
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
|
||||
|
@ -22,11 +22,14 @@
|
||||
#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
|
||||
@ -89,15 +92,21 @@ GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleVal
|
||||
|
||||
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))
|
||||
@ -135,9 +144,53 @@ GetImports(JSContext* cx,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -184,10 +237,11 @@ wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj
|
||||
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
|
||||
@ -511,12 +565,13 @@ WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
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);
|
||||
|
@ -21,15 +21,62 @@
|
||||
#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.
|
||||
@ -158,25 +205,32 @@ Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
||||
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
|
||||
@ -212,6 +266,7 @@ size_t
|
||||
ElemSegment::serializedSize() const
|
||||
{
|
||||
return sizeof(tableIndex) +
|
||||
sizeof(offset) +
|
||||
SerializedPodVectorSize(elems);
|
||||
}
|
||||
|
||||
@ -219,6 +274,7 @@ 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;
|
||||
}
|
||||
@ -227,6 +283,7 @@ 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;
|
||||
}
|
||||
@ -351,7 +408,7 @@ Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
|
||||
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();
|
||||
@ -369,20 +426,53 @@ Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
||||
}
|
||||
|
||||
// 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;
|
||||
@ -468,11 +558,54 @@ Module::instantiateTable(JSContext* cx, HandleWasmTableObject tableImport,
|
||||
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)
|
||||
{
|
||||
@ -523,6 +656,11 @@ CreateExportObject(JSContext* cx,
|
||||
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))
|
||||
@ -537,6 +675,7 @@ Module::instantiate(JSContext* cx,
|
||||
Handle<FunctionVector> funcImports,
|
||||
HandleWasmTableObject tableImport,
|
||||
HandleWasmMemoryObject memoryImport,
|
||||
const ValVector& globalImports,
|
||||
HandleObject instanceProto,
|
||||
MutableHandleWasmInstanceObject instanceObj) const
|
||||
{
|
||||
@ -580,7 +719,8 @@ Module::instantiate(JSContext* cx,
|
||||
maybeBytecode,
|
||||
memory,
|
||||
Move(tables),
|
||||
funcImports);
|
||||
funcImports,
|
||||
globalImports);
|
||||
if (!instance)
|
||||
return false;
|
||||
|
||||
@ -594,7 +734,7 @@ Module::instantiate(JSContext* cx,
|
||||
|
||||
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));
|
||||
@ -611,7 +751,7 @@ Module::instantiate(JSContext* cx,
|
||||
// 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.
|
||||
|
@ -26,6 +26,18 @@
|
||||
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.
|
||||
//
|
||||
@ -113,18 +125,19 @@ 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)
|
||||
};
|
||||
@ -149,11 +162,11 @@ typedef Vector<DataSegment, 0, SystemAllocPolicy> DataSegmentVector;
|
||||
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))
|
||||
{}
|
||||
|
||||
@ -189,7 +202,7 @@ class Module : public RefCounted<Module>
|
||||
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,
|
||||
@ -219,6 +232,7 @@ class Module : public RefCounted<Module>
|
||||
Handle<FunctionVector> funcImports,
|
||||
HandleWasmTableObject tableImport,
|
||||
HandleWasmMemoryObject memoryImport,
|
||||
const ValVector& globalImports,
|
||||
HandleObject instanceProto,
|
||||
MutableHandleWasmInstanceObject instanceObj) const;
|
||||
|
||||
|
@ -86,8 +86,11 @@ class WasmToken
|
||||
Export,
|
||||
Float,
|
||||
Func,
|
||||
GetGlobal,
|
||||
GetLocal,
|
||||
Global,
|
||||
If,
|
||||
Immutable,
|
||||
Import,
|
||||
Index,
|
||||
Memory,
|
||||
@ -105,6 +108,7 @@ class WasmToken
|
||||
Result,
|
||||
Return,
|
||||
Segment,
|
||||
SetGlobal,
|
||||
SetLocal,
|
||||
SignedInteger,
|
||||
Start,
|
||||
@ -978,8 +982,12 @@ WasmTokenStream::next()
|
||||
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':
|
||||
@ -1266,6 +1274,8 @@ WasmTokenStream::next()
|
||||
}
|
||||
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"))
|
||||
@ -1317,6 +1327,8 @@ WasmTokenStream::next()
|
||||
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"))
|
||||
@ -1841,6 +1853,29 @@ ParseGetLocal(WasmParseContext& c)
|
||||
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)
|
||||
{
|
||||
@ -2176,6 +2211,8 @@ ParseExprInsideParens(WasmParseContext& c)
|
||||
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:
|
||||
@ -2184,6 +2221,8 @@ ParseExprInsideParens(WasmParseContext& c)
|
||||
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:
|
||||
@ -2423,6 +2462,20 @@ ParseStartFunc(WasmParseContext& c, WasmToken token, AstModule* module)
|
||||
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)
|
||||
{
|
||||
@ -2458,6 +2511,16 @@ ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
|
||||
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)) {
|
||||
@ -2494,13 +2557,21 @@ ParseExport(WasmParseContext& c)
|
||||
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;
|
||||
}
|
||||
@ -2541,15 +2612,19 @@ ParseTable(WasmParseContext& c, WasmToken token, AstModule* module)
|
||||
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);
|
||||
@ -2560,7 +2635,24 @@ ParseElemSegment(WasmParseContext& c)
|
||||
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*
|
||||
@ -2597,6 +2689,12 @@ ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars*
|
||||
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))
|
||||
@ -2658,6 +2756,7 @@ class Resolver
|
||||
{
|
||||
UniqueChars* error_;
|
||||
AstNameMap varMap_;
|
||||
AstNameMap globalMap_;
|
||||
AstNameMap sigMap_;
|
||||
AstNameMap funcMap_;
|
||||
AstNameMap importMap_;
|
||||
@ -2703,13 +2802,18 @@ class Resolver
|
||||
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();
|
||||
@ -2727,6 +2831,9 @@ class Resolver
|
||||
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);
|
||||
}
|
||||
@ -2755,6 +2862,11 @@ class Resolver
|
||||
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;
|
||||
@ -2887,6 +2999,24 @@ ResolveSetLocal(Resolver& r, AstSetLocal& sl)
|
||||
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)
|
||||
{
|
||||
@ -3006,6 +3136,8 @@ ResolveExpr(Resolver& r, AstExpr& expr)
|
||||
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:
|
||||
@ -3014,6 +3146,8 @@ ResolveExpr(Resolver& r, AstExpr& expr)
|
||||
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:
|
||||
@ -3033,8 +3167,7 @@ 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");
|
||||
}
|
||||
@ -3078,29 +3211,50 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (AstExport* export_ : module->exports()) {
|
||||
if (export_->kind() != DefinitionKind::Function)
|
||||
continue;
|
||||
if (!r.resolveFunction(export_->func()))
|
||||
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()) {
|
||||
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;
|
||||
@ -3111,6 +3265,11 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AstElemSegment* segment : module->elemSegments()) {
|
||||
if (!ResolveExpr(r, *segment->offset()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3261,6 +3420,21 @@ EncodeSetLocal(Encoder& e, AstSetLocal& sl)
|
||||
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)
|
||||
{
|
||||
@ -3427,6 +3601,8 @@ EncodeExpr(Encoder& e, AstExpr& expr)
|
||||
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:
|
||||
@ -3435,6 +3611,8 @@ EncodeExpr(Encoder& e, AstExpr& expr)
|
||||
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:
|
||||
@ -3569,6 +3747,13 @@ EncodeImport(Encoder& e, bool newFormat, AstImport& imp)
|
||||
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()))
|
||||
@ -3640,6 +3825,34 @@ EncodeMemorySection(Encoder& e, bool newFormat, AstModule& module)
|
||||
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)
|
||||
{
|
||||
@ -3647,7 +3860,7 @@ EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
|
||||
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()))
|
||||
@ -3664,7 +3877,8 @@ EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
|
||||
|
||||
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:
|
||||
@ -3868,9 +4082,9 @@ 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()))
|
||||
@ -3932,6 +4146,9 @@ EncodeModule(AstModule& module, bool newFormat, Bytes* bytes)
|
||||
if (!EncodeMemorySection(e, newFormat, module))
|
||||
return false;
|
||||
|
||||
if (!EncodeGlobalSection(e, module))
|
||||
return false;
|
||||
|
||||
if (!EncodeExportSection(e, newFormat, module))
|
||||
return false;
|
||||
|
||||
|
@ -37,7 +37,7 @@ 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:
|
||||
|
@ -361,9 +361,11 @@ class Val
|
||||
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
|
||||
@ -413,6 +415,142 @@ struct SigHashPolicy
|
||||
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
|
||||
@ -468,21 +606,6 @@ struct SigWithId : Sig
|
||||
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.
|
||||
|
||||
@ -940,6 +1063,7 @@ static const unsigned InitialGlobalDataBytes = NaN32GlobalDataOffset + sizeo
|
||||
|
||||
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;
|
||||
|
@ -36,8 +36,9 @@ function jsify(wasmVal) {
|
||||
// - 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') {
|
||||
|
209
js/src/jit-test/tests/wasm/globals.js
Normal file
209
js/src/jit-test/tests/wasm/globals.js
Normal file
@ -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/);
|
||||
}
|
||||
|
@ -220,8 +220,8 @@ var code = textToBinary(`(module
|
||||
(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)
|
||||
@ -267,6 +267,7 @@ 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/);
|
||||
|
||||
@ -309,8 +310,8 @@ assertEq(i8[102], 0x0);
|
||||
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))
|
||||
|
@ -21,7 +21,7 @@ var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
|
||||
// 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);
|
||||
@ -59,7 +59,7 @@ 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);
|
||||
|
@ -14,13 +14,28 @@ const evalText = (str, imports) => new Instance(new Module(textToBinary(str)), i
|
||||
|
||||
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}))`;
|
||||
@ -29,17 +44,17 @@ var call = evalText(`(module (table (resizable 10)) ${callee(0)} ${caller})`).ex
|
||||
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);
|
||||
@ -50,7 +65,7 @@ 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);
|
||||
|
@ -2139,6 +2139,8 @@ jit::MakeMRegExpHoistable(MIRGraph& graph)
|
||||
// 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);
|
||||
|
||||
|
@ -4083,13 +4083,20 @@ LIRGenerator::visitHasClass(MHasClass* 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
|
||||
|
@ -8062,6 +8062,15 @@ class LWasmLoadGlobalVar : public LInstructionHelper<1, 0, 0>
|
||||
}
|
||||
};
|
||||
|
||||
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:
|
||||
@ -8077,6 +8086,19 @@ class LWasmStoreGlobalVar : public LInstructionHelper<0, 1, 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:
|
||||
|
@ -390,7 +390,9 @@
|
||||
_(WasmStore) \
|
||||
_(WasmBoundsCheck) \
|
||||
_(WasmLoadGlobalVar) \
|
||||
_(WasmLoadGlobalVarI64) \
|
||||
_(WasmStoreGlobalVar) \
|
||||
_(WasmStoreGlobalVarI64) \
|
||||
_(AsmJSLoadHeap) \
|
||||
_(AsmJSStoreHeap) \
|
||||
_(AsmJSLoadFuncPtr) \
|
||||
|
@ -1211,6 +1211,15 @@ CodeGeneratorX64::visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins)
|
||||
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)
|
||||
{
|
||||
@ -1246,6 +1255,16 @@ CodeGeneratorX64::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
|
||||
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)
|
||||
{
|
||||
|
@ -77,6 +77,8 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
|
||||
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);
|
||||
|
@ -5554,7 +5554,7 @@ static void DebugPaintItem(DrawTarget& aDrawTarget,
|
||||
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) {
|
||||
|
@ -1351,6 +1351,7 @@ public:
|
||||
, mScrollClip(nullptr)
|
||||
, mReferenceFrame(nullptr)
|
||||
, mAnimatedGeometryRoot(nullptr)
|
||||
, mForceNotVisible(false)
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
, mPainted(false)
|
||||
#endif
|
||||
|
@ -6541,7 +6541,7 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
|
||||
// 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 =
|
||||
|
@ -307,8 +307,8 @@ nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
|
||||
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);
|
||||
|
@ -403,7 +403,7 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
|
||||
// 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);
|
||||
|
||||
|
@ -587,7 +587,7 @@ CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
|
||||
|
||||
mIsDirty = true;
|
||||
|
||||
int64_t fileSize = kChunkSize * mIndex + aOffset + aLen;
|
||||
int64_t fileSize = static_cast<int64_t>(kChunkSize) * mIndex + aOffset + aLen;
|
||||
bool notify = false;
|
||||
|
||||
if (fileSize > mFile->mDataSize) {
|
||||
|
@ -60,11 +60,6 @@ function contentHandler(metadata, response)
|
||||
|
||||
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);
|
||||
|
||||
|
@ -65,6 +65,11 @@ function run_test()
|
||||
|
||||
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();
|
||||
|
@ -55,16 +55,16 @@ function contentHandler(metadata, response)
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
@ -282,6 +282,7 @@ SandboxInfo::SubmitTelemetry()
|
||||
sandboxInfo.Test(SandboxInfo::kEnabledForMedia));
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
void
|
||||
SandboxInfo::AnnotateCrashReport() const
|
||||
{
|
||||
@ -291,5 +292,6 @@ SandboxInfo::AnnotateCrashReport() const
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -57,7 +57,9 @@ public:
|
||||
return !Test(kEnabledForMedia) || Test(kHasSeccompBPF);
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
MOZ_EXPORT void AnnotateCrashReport() const;
|
||||
#endif
|
||||
|
||||
static void SubmitTelemetry();
|
||||
|
||||
|
@ -17,6 +17,7 @@ echo "running as" $(id)
|
||||
: NEED_WINDOW_MANAGER ${NEED_WINDOW_MANAGER:=false}
|
||||
: NEED_PULSEAUDIO ${NEED_PULSEAUDIO:=false}
|
||||
: START_VNC ${START_VNC:=false}
|
||||
: TASKCLUSTER_INTERACTIVE ${TASKCLUSTER_INTERACTIVE:=false}
|
||||
: WORKSPACE ${WORKSPACE:=/home/worker/workspace}
|
||||
: mozharness args "${@}"
|
||||
|
||||
@ -42,10 +43,12 @@ cleanup() {
|
||||
# To share X issues
|
||||
cp /home/worker/.xsession-errors ~/artifacts/public/xsession-errors.log
|
||||
fi
|
||||
# When you call this script with START_VNC we make sure we
|
||||
# don't kill xvfb so you don't lose your VNC connection
|
||||
if [ -n "$xvfb_pid" ] && [ $START_VNC == false ] ; then
|
||||
# When you call this script with START_VNC or TASKCLUSTER_INTERACTIVE
|
||||
# we make sure we don't kill xvfb so you don't lose your connection
|
||||
xvfb_pid=`pidof Xvfb`
|
||||
if [ -n "$xvfb_pid" ] && [ $START_VNC == false ] && [ $TASKCLUSTER_INTERACTIVE == false ] ; then
|
||||
kill $xvfb_pid || true
|
||||
screen -XS xvfb quit || true
|
||||
fi
|
||||
exit $rv
|
||||
}
|
||||
@ -70,12 +73,11 @@ if $NEED_PULSEAUDIO; then
|
||||
pactl load-module module-null-sink
|
||||
fi
|
||||
|
||||
# run XVfb in the background, if necessary
|
||||
# run Xvfb in the background, if necessary
|
||||
if $NEED_XVFB; then
|
||||
Xvfb :0 -nolisten tcp -screen 0 1600x1200x24 \
|
||||
> ~/artifacts/public/xvfb.log 2>&1 &
|
||||
screen -dmS xvfb Xvfb :0 -nolisten tcp -screen 0 1600x1200x24 \
|
||||
> ~/artifacts/public/xvfb.log 2>&1
|
||||
export DISPLAY=:0
|
||||
xvfb_pid=$!
|
||||
# Only error code 255 matters, because it signifies that no
|
||||
# display could be opened. As long as we can open the display
|
||||
# tests should work. We'll retry a few times with a sleep before
|
||||
@ -146,7 +148,7 @@ exec \${cmd}" > ${mozharness_bin}
|
||||
chmod +x ${mozharness_bin}
|
||||
|
||||
# In interactive mode, the user will be prompted with options for what to do.
|
||||
if [ "$TASKCLUSTER_INTERACTIVE" != "true" ]; then
|
||||
if ! $TASKCLUSTER_INTERACTIVE; then
|
||||
# run the given mozharness script and configs, but pass the rest of the
|
||||
# arguments in from our own invocation
|
||||
${mozharness_bin};
|
||||
|
@ -19,6 +19,9 @@ from taskgraph.util.time import (
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# the maximum number of parallel createTask calls to make
|
||||
CONCURRENCY = 50
|
||||
|
||||
|
||||
def create_tasks(taskgraph, label_to_taskid):
|
||||
# TODO: use the taskGroupId of the decision task
|
||||
@ -29,7 +32,7 @@ def create_tasks(taskgraph, label_to_taskid):
|
||||
|
||||
decision_task_id = os.environ.get('TASK_ID')
|
||||
|
||||
with futures.ThreadPoolExecutor(requests.adapters.DEFAULT_POOLSIZE) as e:
|
||||
with futures.ThreadPoolExecutor(CONCURRENCY) as e:
|
||||
fs = {}
|
||||
|
||||
# We can't submit a task until its dependencies have been submitted.
|
||||
|
@ -21,8 +21,8 @@ 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();
|
||||
|
@ -507,10 +507,10 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWidget* widget)
|
||||
// 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],
|
||||
@ -625,8 +625,8 @@ AndroidGeckoEvent::MakeMouseEvent(nsIWidget* widget)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1620,8 +1620,9 @@ nsWindow::Resize(double aX,
|
||||
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())
|
||||
|
@ -3746,7 +3746,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
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);
|
||||
@ -4126,7 +4126,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
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 {
|
||||
@ -5770,7 +5770,7 @@ PanGestureTypeForEvent(NSEvent* aEvent)
|
||||
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"
|
||||
|
@ -477,7 +477,7 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, ui
|
||||
|
||||
// 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);
|
||||
|
@ -146,7 +146,7 @@ already_AddRefed<PrintTarget> nsDeviceContextSpecX::MakePrintTarget()
|
||||
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);
|
||||
|
@ -161,7 +161,7 @@ already_AddRefed<PrintTarget> nsDeviceContextSpecGTK::MakePrintTarget()
|
||||
}
|
||||
}
|
||||
|
||||
IntSize size(width, height);
|
||||
IntSize size = IntSize::Truncate(width, height);
|
||||
|
||||
if (format == nsIPrintSettings::kOutputFormatPDF) {
|
||||
return PrintTargetPDF::CreateOrNull(stream, size);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user