Merge inbound to central, a=merge

MozReview-Commit-ID: 2MDU9VGB9Pg
This commit is contained in:
Wes Kocher 2017-09-22 14:56:05 -07:00
commit 1443004d66
121 changed files with 2459 additions and 14606 deletions

View File

@ -1030,7 +1030,11 @@ pref("dom.ipc.plugins.sandbox-level.flash", 0);
// On windows these levels are:
// See - security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
// SetSecurityLevelForContentProcess() for what the different settings mean.
#if defined(NIGHTLY_BUILD)
pref("security.sandbox.content.level", 4);
#else
pref("security.sandbox.content.level", 3);
#endif
// This controls the depth of stack trace that is logged when Windows sandbox
// logging is turned on. This is only currently available for the content

View File

@ -313,7 +313,7 @@ Sanitizer.prototype = {
let uri = principal.URI;
if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") {
promises.push(new Promise(r => {
let req = quotaManagerService.clearStoragesForPrincipal(principal, null, true);
let req = quotaManagerService.clearStoragesForPrincipal(principal, null, false);
req.callback = () => { r(); };
}));
}

View File

@ -93,7 +93,7 @@ const clearIndexedDB = async function(options) {
let uri = principal.URI;
if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") {
promises.push(new Promise(r => {
let req = quotaManagerService.clearStoragesForPrincipal(principal, null, true);
let req = quotaManagerService.clearStoragesForPrincipal(principal, null, false);
req.callback = () => { r(); };
}));
}

View File

@ -1611,6 +1611,7 @@ nsDocShell::LoadURI(nsIURI* aURI,
nullptr, // No type hint
VoidString(), // No forced download
postStream,
-1, // XXXbaku
headersStream,
loadType,
nullptr, // No SHEntry
@ -5488,9 +5489,9 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
mozilla::net::RP_Unset,
nsContentUtils::GetSystemPrincipal(), nullptr,
INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
nullptr, VoidString(), nullptr, nullptr, LOAD_ERROR_PAGE,
nullptr, true, VoidString(), this, nullptr, false,
nullptr, nullptr);
nullptr, VoidString(), nullptr, -1, nullptr,
LOAD_ERROR_PAGE, nullptr, true, VoidString(), this,
nullptr, false, nullptr, nullptr);
}
NS_IMETHODIMP
@ -5591,6 +5592,7 @@ nsDocShell::Reload(uint32_t aReloadFlags)
NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
VoidString(), // No forced download
nullptr, // No post data
-1, // No post data length
nullptr, // No headers data
loadType, // Load type
nullptr, // No SHEntry
@ -9709,6 +9711,7 @@ public:
uint32_t aFlags,
const char* aTypeHint,
nsIInputStream* aPostData,
int64_t aPostDataLength,
nsIInputStream* aHeadersData,
uint32_t aLoadType,
nsISHEntry* aSHEntry,
@ -9729,6 +9732,7 @@ public:
, mTriggeringPrincipal(aTriggeringPrincipal)
, mPrincipalToInherit(aPrincipalToInherit)
, mPostData(aPostData)
, mPostDataLength(aPostDataLength)
, mHeadersData(aHeadersData)
, mSHEntry(aSHEntry)
, mFlags(aFlags)
@ -9757,10 +9761,11 @@ public:
mFlags, EmptyString(),
mTypeHint.IsVoid() ? nullptr
: mTypeHint.get(),
VoidString(), mPostData, mHeadersData,
mLoadType, mSHEntry, mFirstParty,
mSrcdoc, mSourceDocShell, mBaseURI,
mCheckForPrerender, nullptr, nullptr);
VoidString(), mPostData, mPostDataLength,
mHeadersData, mLoadType, mSHEntry,
mFirstParty, mSrcdoc, mSourceDocShell,
mBaseURI, mCheckForPrerender, nullptr,
nullptr);
}
private:
@ -9777,6 +9782,7 @@ private:
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
nsCOMPtr<nsIInputStream> mPostData;
int64_t mPostDataLength;
nsCOMPtr<nsIInputStream> mHeadersData;
nsCOMPtr<nsISHEntry> mSHEntry;
uint32_t mFlags;
@ -9828,6 +9834,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
const char* aTypeHint,
const nsAString& aFileName,
nsIInputStream* aPostData,
int64_t aPostDataLength,
nsIInputStream* aHeadersData,
uint32_t aLoadType,
nsISHEntry* aSHEntry,
@ -10228,6 +10235,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
aTypeHint,
VoidString(), // No forced download
aPostData,
aPostDataLength,
aHeadersData,
aLoadType,
aSHEntry,
@ -10319,9 +10327,9 @@ nsDocShell::InternalLoad(nsIURI* aURI,
new InternalLoadEvent(this, aURI, aOriginalURI, aResultPrincipalURI,
aLoadReplace, aReferrer, aReferrerPolicy,
aTriggeringPrincipal, principalToInherit,
aFlags, aTypeHint, aPostData, aHeadersData,
aLoadType, aSHEntry, aFirstParty, aSrcdoc,
aSourceDocShell, aBaseURI, false);
aFlags, aTypeHint, aPostData, aPostDataLength,
aHeadersData, aLoadType, aSHEntry, aFirstParty,
aSrcdoc, aSourceDocShell, aBaseURI, false);
return DispatchToTabGroup(TaskCategory::Other, ev.forget());
}
@ -10705,9 +10713,9 @@ nsDocShell::InternalLoad(nsIURI* aURI,
new InternalLoadEvent(this, aURI, aOriginalURI, aResultPrincipalURI,
aLoadReplace, aReferrer, aReferrerPolicy,
aTriggeringPrincipal, principalToInherit,
aFlags, aTypeHint, aPostData, aHeadersData,
aLoadType, aSHEntry, aFirstParty, aSrcdoc,
aSourceDocShell, aBaseURI, false);
aFlags, aTypeHint, aPostData, aPostDataLength,
aHeadersData, aLoadType, aSHEntry, aFirstParty,
aSrcdoc, aSourceDocShell, aBaseURI, false);
// We don't need any success handler since in that case
// OnPartialSHistoryDeactive would be called, and it would ensure
// docshell loads about:blank.
@ -10858,7 +10866,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
!(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
aReferrerPolicy,
aTriggeringPrincipal, principalToInherit, aTypeHint,
aFileName, aPostData, aHeadersData,
aFileName, aPostData, aPostDataLength, aHeadersData,
aFirstParty, aDocShell, getter_AddRefs(req),
(aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
(aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
@ -11000,6 +11008,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
const char* aTypeHint,
const nsAString& aFileName,
nsIInputStream* aPostData,
int64_t aPostDataLength,
nsIInputStream* aHeadersData,
bool aFirstParty,
nsIDocShell** aDocShell,
@ -11426,7 +11435,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
}
// we really need to have a content type associated with this stream!!
postChannel->SetUploadStream(aPostData, EmptyCString(), -1);
postChannel->SetUploadStream(aPostData, EmptyCString(), aPostDataLength);
}
/* If there is a valid postdata *and* it is a History Load,
@ -12886,6 +12895,7 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType)
contentType.get(), // Type hint
VoidString(), // No forced file download
postData, // Post data stream
-1, // Post data stream length
nullptr, // No headers stream
aLoadType, // Load type
aEntry, // SHEntry
@ -14175,6 +14185,7 @@ public:
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
int64_t aPostDataStreamLength,
nsIInputStream* aHeadersDataStream,
bool aNoOpenerImplied,
bool aIsTrusted,
@ -14193,8 +14204,8 @@ public:
if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
mHandler->OnLinkClickSync(mContent, mURI,
mTargetSpec.get(), mFileName,
mPostDataStream, mHeadersDataStream,
mNoOpenerImplied,
mPostDataStream, mPostDataStreamLength,
mHeadersDataStream, mNoOpenerImplied,
nullptr, nullptr, mTriggeringPrincipal);
}
return NS_OK;
@ -14206,6 +14217,7 @@ private:
nsString mTargetSpec;
nsString mFileName;
nsCOMPtr<nsIInputStream> mPostDataStream;
int64_t mPostDataStreamLength;
nsCOMPtr<nsIInputStream> mHeadersDataStream;
nsCOMPtr<nsIContent> mContent;
PopupControlState mPopupState;
@ -14220,6 +14232,7 @@ OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
int64_t aPostDataStreamLength,
nsIInputStream* aHeadersDataStream,
bool aNoOpenerImplied,
bool aIsTrusted,
@ -14230,6 +14243,7 @@ OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
, mTargetSpec(aTargetSpec)
, mFileName(aFileName)
, mPostDataStream(aPostDataStream)
, mPostDataStreamLength(aPostDataStreamLength)
, mHeadersDataStream(aHeadersDataStream)
, mContent(aContent)
, mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
@ -14245,6 +14259,7 @@ nsDocShell::OnLinkClick(nsIContent* aContent,
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
int64_t aPostDataStreamLength,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted,
nsIPrincipal* aTriggeringPrincipal)
@ -14290,7 +14305,8 @@ nsDocShell::OnLinkClick(nsIContent* aContent,
nsCOMPtr<nsIRunnable> ev =
new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
aPostDataStream, aHeadersDataStream, noOpenerImplied,
aPostDataStream, aPostDataStreamLength,
aHeadersDataStream, noOpenerImplied,
aIsTrusted, aTriggeringPrincipal);
return DispatchToTabGroup(TaskCategory::UI, ev.forget());
}
@ -14301,6 +14317,7 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
int64_t aPostDataStreamLength,
nsIInputStream* aHeadersDataStream,
bool aNoOpenerImplied,
nsIDocShell** aDocShell,
@ -14450,6 +14467,7 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
NS_LossyConvertUTF16toASCII(typeHint).get(),
aFileName, // Download as file
aPostDataStream, // Post data stream
aPostDataStreamLength, // Post data stream length
aHeadersDataStream, // Headers stream
LOAD_LINK, // Load type
nullptr, // No SHEntry

View File

@ -210,6 +210,7 @@ public:
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
int64_t aPostDataStreamLength,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted,
nsIPrincipal* aTriggeringPrincipal) override;
@ -218,6 +219,7 @@ public:
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream = 0,
int64_t aPostDataStreamLength = -1,
nsIInputStream* aHeadersDataStream = 0,
bool aNoOpenerImplied = false,
nsIDocShell** aDocShell = 0,
@ -435,6 +437,7 @@ protected:
const char* aTypeHint,
const nsAString& aFileName,
nsIInputStream* aPostData,
int64_t aPostDataLength,
nsIInputStream* aHeadersData,
bool aFirstParty,
nsIDocShell** aDocShell,

View File

@ -173,6 +173,8 @@ interface nsIDocShell : nsIDocShellTreeItem
* @param aFileName - Non-null when the link should be downloaded as
the given filename.
* @param aPostDataStream - Post data stream (if POSTing)
* @param aPostDataStreamLength - Post data stream length. Use -1 if the length
of the stream is unknown.
* @param aHeadersStream - Stream containing "extra" request headers...
* @param aLoadFlags - Flags to modify load behaviour. Flags are defined
* in nsIWebNavigation.
@ -198,6 +200,7 @@ interface nsIDocShell : nsIDocShellTreeItem
in string aTypeHint,
in AString aFileName,
in nsIInputStream aPostDataStream,
in long long aPostDataStreamLength,
in nsIInputStream aHeadersStream,
in unsigned long aLoadFlags,
in nsISHEntry aSHEntry,

View File

@ -33,8 +33,10 @@ public:
* @param aURI a URI object that defines the destination for the link
* @param aTargetSpec indicates where the link is targeted (may be an empty
* string)
* @param aPostDataStream the POST data to send
* @param aFileName non-null when the link should be downloaded as the given file
* @param aPostDataStream the POST data to send
* @param aPostDataStreamLength the POST data length. Use -1 if the length is
* unknown.
* @param aHeadersDataStream ???
* @param aIsTrusted false if the triggerer is an untrusted DOM event.
* @param aTriggeringPrincipal, if not passed explicitly we fall back to
@ -45,6 +47,7 @@ public:
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream,
int64_t aPostDataStreamLength,
nsIInputStream* aHeadersDataStream,
bool aIsTrusted,
nsIPrincipal* aTriggeringPrincipal) = 0;
@ -61,6 +64,7 @@ public:
* string)
* @param aFileName non-null when the link should be downloaded as the given file
* @param aPostDataStream the POST data to send
* @param aPostDataStreamLength the POST data length
* @param aHeadersDataStream ???
* @param aNoOpenerImplied if the link implies "noopener"
* @param aDocShell (out-param) the DocShell that the request was opened on
@ -73,6 +77,7 @@ public:
const char16_t* aTargetSpec,
const nsAString& aFileName,
nsIInputStream* aPostDataStream = 0,
int64_t aPostDataStreamLength = -1,
nsIInputStream* aHeadersDataStream = 0,
bool aNoOpenerImplied = false,
nsIDocShell** aDocShell = 0,

View File

@ -103,7 +103,8 @@ NS_INTERFACE_MAP_END
// HTMLFormSubmission
nsresult
FormData::GetEncodedSubmission(nsIURI* aURI,
nsIInputStream** aPostDataStream)
nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength)
{
NS_NOTREACHED("Shouldn't call FormData::GetEncodedSubmission");
return NS_OK;

View File

@ -113,7 +113,8 @@ public:
// HTMLFormSubmission
virtual nsresult
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength) override;
virtual nsresult AddNameValuePair(const nsAString& aName,
const nsAString& aValue) override

View File

@ -217,7 +217,6 @@ pref(clipboard.autocopy,true) load 1370737.html
pref(dom.IntersectionObserver.enabled,true) load 1370968.html
load 1377826.html
skip-if(stylo&&isDebugBuild&&winWidget) load structured_clone_container_throws.html # Bug 1383845
HTTP(..) load xhr_abortinprogress.html
load xhr_empty_datauri.html
load xhr_html_nullresponse.html
load 1383478.html

View File

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset=UTF-8>
<script>"use strict";
(function(){
var req = new XMLHttpRequest();
req.open('GET', '?' + Date.now());
req.responseType = 'moz-blob';
var b = null;
req.onprogress = function(e) {
b = req.response;
};
req.onreadystatechange = function(e) {
if (req.readyState == 3) {
req.abort();
}
if (req.readyState == 4) {
document.documentElement.removeAttribute('class');
}
};
req.send();
})();
</script>

View File

@ -161,6 +161,6 @@ DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest state must be OPENED.", NS_ERROR
DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest must not be sending.", NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING)
DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest state must not be LOADING or DONE.", NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE)
DOM4_MSG_DEF(InvalidStateError, "responseXML is only available if responseType is '' or 'document'.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML)
DOM4_MSG_DEF(InvalidStateError, "responseText is only available if responseType is '', 'document', or 'moz-chunked-text'.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT)
DOM4_MSG_DEF(InvalidStateError, "synchronous XMLHttpRequests do not support 'moz-chunked-text' or 'moz-chunked-arraybuffer' responseType.", NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC)
DOM4_MSG_DEF(InvalidStateError, "responseText is only available if responseType is '' or 'document'.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT)
DOM4_MSG_DEF(InvalidStateError, "synchronous XMLHttpRequests do not support 'moz-chunked-arraybuffer' responseType.", NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC)
DOM4_MSG_DEF(InvalidAccessError, "synchronous XMLHttpRequests do not support timeout and responseType.", NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC)

View File

@ -5550,7 +5550,8 @@ nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
handler->OnLinkClick(aContent, aLinkURI,
fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
fileName, nullptr, nullptr, aIsTrusted, aContent->NodePrincipal());
fileName, nullptr, -1, nullptr, aIsTrusted,
aContent->NodePrincipal());
}
}

View File

@ -87,6 +87,8 @@ support-files =
support-files = pointerevent_releasepointercapture_onpointerup_mouse-manual.html
[test_pointerevent_releasepointercapture_release_right_after_capture-manual.html]
support-files = pointerevent_releasepointercapture_release_right_after_capture-manual.html
[test_pointerevent_sequence_at_implicit_release_on_drag-manual.html]
support-files = pointerevent_sequence_at_implicit_release_on_drag-manual.html
[test_pointerevent_setpointercapture_disconnected-manual.html]
support-files = pointerevent_setpointercapture_disconnected-manual.html
[test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]

View File

@ -186,6 +186,7 @@ function sendTouchEvent(int_win, elemId, touchEventType, params) {
TouchEventHelper.TOUCH_STATE = true; // Set touch flag.
break;
case "touchend":
case "touchcancel":
TouchEventHelper.TOUCH_STATE = false; // Clear touch flag.
break;
}

View File

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>W3C pointerevent_sequence_at_implicit_release_on_drag-manual.html in Mochitest form</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="mochitest_support_external.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
function startTest() {
runTestInNewWindow("pointerevent_sequence_at_implicit_release_on_drag-manual.html");
}
function executeTest(int_win) {
sendTouchEvent(int_win, "target", "touchstart");
sendTouchEvent(int_win, "target", "touchmove");
sendTouchEvent(int_win, "target", "touchmove");
sendTouchEvent(int_win, "target", "touchcancel");
sendMouseEvent(int_win, "done", "mousedown");
sendMouseEvent(int_win, "done", "mouseup");
}
</script>
</head>
<body>
</body>
</html>

View File

@ -162,21 +162,13 @@ IPCBlobInputStream::Available(uint64_t* aLength)
{
// We don't have a remoteStream yet. Let's return the full known size.
if (mState == eInit || mState == ePending) {
*aLength = mLength;
*aLength = 0;
return NS_OK;
}
if (mState == eRunning) {
MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
// This will go away eventually: an async input stream can return 0 in
// Available(), but this is not currently fully supported in the rest of
// gecko.
if (!mAsyncRemoteStream) {
*aLength = mLength;
return NS_OK;
}
nsresult rv = EnsureAsyncRemoteStream();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;

View File

@ -796,14 +796,17 @@ HTMLFormElement::SubmitSubmission(HTMLFormSubmission* aFormSubmission)
nullptr, doc);
nsCOMPtr<nsIInputStream> postDataStream;
int64_t postDataStreamLength = -1;
rv = aFormSubmission->GetEncodedSubmission(actionURI,
getter_AddRefs(postDataStream));
getter_AddRefs(postDataStream),
&postDataStreamLength);
NS_ENSURE_SUBMIT_SUCCESS(rv);
rv = linkHandler->OnLinkClickSync(this, actionURI,
target.get(),
VoidString(),
postDataStream, nullptr, false,
postDataStream, postDataStreamLength,
nullptr, false,
getter_AddRefs(docShell),
getter_AddRefs(mSubmittingRequest));
NS_ENSURE_SUBMIT_SUCCESS(rv);

View File

@ -112,7 +112,8 @@ public:
AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
virtual nsresult
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength) override;
protected:
@ -266,11 +267,13 @@ HandleMailtoSubject(nsCString& aPath)
nsresult
FSURLEncoded::GetEncodedSubmission(nsIURI* aURI,
nsIInputStream** aPostDataStream)
nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength)
{
nsresult rv = NS_OK;
*aPostDataStream = nullptr;
*aPostDataStreamLength = -1;
if (mMethod == NS_FORM_METHOD_POST) {
@ -313,6 +316,8 @@ FSURLEncoded::GetEncodedSubmission(nsIURI* aURI,
*aPostDataStream = mimeStream;
NS_ADDREF(*aPostDataStream);
*aPostDataStreamLength = mQueryString.Length();
}
} else {
@ -615,7 +620,8 @@ FSMultipartFormData::AddDataChunk(const nsACString& aName,
nsresult
FSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
nsIInputStream** aPostDataStream)
nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength)
{
nsresult rv;
@ -627,8 +633,10 @@ FSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
nsAutoCString contentType;
GetContentType(contentType);
mimeStream->AddHeader("Content-Type", contentType.get());
uint64_t unused;
mimeStream->SetData(GetSubmissionBody(&unused));
uint64_t bodySize;
mimeStream->SetData(GetSubmissionBody(&bodySize));
*aPostDataStreamLength = bodySize;
mimeStream.forget(aPostDataStream);
@ -677,7 +685,8 @@ public:
AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
virtual nsresult
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
int64_t* aPostDataStreaLength) override;
private:
nsString mBody;
@ -716,10 +725,14 @@ FSTextPlain::AddNameDirectoryPair(const nsAString& aName,
nsresult
FSTextPlain::GetEncodedSubmission(nsIURI* aURI,
nsIInputStream** aPostDataStream)
nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength)
{
nsresult rv = NS_OK;
*aPostDataStream = nullptr;
*aPostDataStreamLength = -1;
// XXX HACK We are using the standard URL mechanism to give the body to the
// mailer instead of passing the post data stream to it, since that sounds
// hard.
@ -770,6 +783,8 @@ FSTextPlain::GetEncodedSubmission(nsIURI* aURI,
mimeStream->AddHeader("Content-Type", "text/plain");
mimeStream->SetData(bodyStream);
CallQueryInterface(mimeStream, aPostDataStream);
*aPostDataStreamLength = cbody.Length();
}
return rv;

View File

@ -84,9 +84,11 @@ public:
*
* @param aURI the URI being submitted to [INOUT]
* @param aPostDataStream a data stream for POST data [OUT]
* @param aPostDataStreamLength a data stream for POST data length [OUT]
*/
virtual nsresult
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) = 0;
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength) = 0;
/**
* Get the charset that will be used for submission.
@ -165,7 +167,8 @@ public:
AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
virtual nsresult
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
int64_t* aPostDataStreamLength) override;
void GetContentType(nsACString& aContentType)
{

View File

@ -104,10 +104,10 @@ GetJSValFromKeyPathString(JSContext* aCx,
bool hasProp;
if (!targetObject) {
// We're still walking the chain of existing objects
// http://w3c.github.io/IndexedDB/#dfn-evaluate-a-key-path-on-a-value
// http://w3c.github.io/IndexedDB/#evaluate-a-key-path-on-a-value
// step 4 substep 1: check for .length on a String value.
if (currentVal.isString() && !tokenizer.hasMoreTokens() &&
token.EqualsLiteral("length") && aOptions == DoNotCreateProperties) {
token.EqualsLiteral("length")) {
aKeyJSVal->setNumber(double(JS_GetStringLength(currentVal.toString())));
break;
}

View File

@ -492,7 +492,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL,
}
rv = lh->OnLinkClick(content, uri, unitarget.get(), VoidString(),
aPostStream, headersDataStream, true, triggeringPrincipal);
aPostStream, -1, headersDataStream, true, triggeringPrincipal);
return rv;
}

View File

@ -23,9 +23,7 @@ enum XMLHttpRequestResponseType {
"text",
// Mozilla-specific stuff
"moz-chunked-text",
"moz-chunked-arraybuffer",
"moz-blob"
};
/**

View File

@ -315,7 +315,6 @@ XMLHttpRequestMainThread::ResetResponse()
TruncateResponseText();
mResponseBlob = nullptr;
mBlobStorage = nullptr;
mBlobSet = nullptr;
mResultArrayBuffer = nullptr;
mArrayBufferBuilder.reset();
mResultJSON.setUndefined();
@ -502,8 +501,7 @@ XMLHttpRequestMainThread::DetectCharset()
if (mResponseType != XMLHttpRequestResponseType::_empty &&
mResponseType != XMLHttpRequestResponseType::Text &&
mResponseType != XMLHttpRequestResponseType::Json &&
mResponseType != XMLHttpRequestResponseType::Moz_chunked_text) {
mResponseType != XMLHttpRequestResponseType::Json) {
return NS_OK;
}
@ -608,18 +606,11 @@ XMLHttpRequestMainThread::GetResponseText(XMLHttpRequestStringSnapshot& aSnapsho
aSnapshot.Reset();
if (mResponseType != XMLHttpRequestResponseType::_empty &&
mResponseType != XMLHttpRequestResponseType::Text &&
mResponseType != XMLHttpRequestResponseType::Moz_chunked_text) {
mResponseType != XMLHttpRequestResponseType::Text) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT);
return;
}
if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text &&
!mInLoadProgressEvent) {
aSnapshot.SetVoid();
return;
}
if (mState != State::loading && mState != State::done) {
return;
}
@ -682,31 +673,6 @@ XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext* aCx)
return NS_OK;
}
void
XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv)
{
// mBlobSet can be null if the request has been canceled
if (!mBlobSet) {
return;
}
nsAutoCString contentType;
if (mState == State::done) {
mChannel->GetContentType(contentType);
}
nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
RefPtr<BlobImpl> blobImpl =
MultipartBlobImpl::Create(Move(subImpls),
NS_ConvertASCIItoUTF16(contentType),
aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
mResponseBlob = Blob::Create(GetOwner(), blobImpl);
}
NS_IMETHODIMP XMLHttpRequestMainThread::GetResponseType(nsAString& aResponseType)
{
MOZ_ASSERT(mResponseType < XMLHttpRequestResponseType::EndGuard_);
@ -748,18 +714,13 @@ XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseTy
}
if (mFlagSynchronous &&
(aResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC);
return;
}
// We want to get rid of this moz-only types. Bug 1335365.
if (aResponseType == XMLHttpRequestResponseType::Moz_blob) {
Telemetry::Accumulate(Telemetry::MOZ_BLOB_IN_XHR, 1);
} else if (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
Telemetry::Accumulate(Telemetry::MOZ_CHUNKED_TEXT_IN_XHR, 1);
} else if (aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
if (aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
Telemetry::Accumulate(Telemetry::MOZ_CHUNKED_ARRAYBUFFER_IN_XHR, 1);
}
@ -783,7 +744,6 @@ XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
switch (mResponseType) {
case XMLHttpRequestResponseType::_empty:
case XMLHttpRequestResponseType::Text:
case XMLHttpRequestResponseType::Moz_chunked_text:
{
DOMString str;
GetResponseText(str, aRv);
@ -818,17 +778,10 @@ XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
return;
}
case XMLHttpRequestResponseType::Blob:
case XMLHttpRequestResponseType::Moz_blob:
{
if (mState != State::done) {
if (mResponseType != XMLHttpRequestResponseType::Moz_blob) {
aResponse.setNull();
return;
}
if (!mResponseBlob) {
CreatePartialBlob(aRv);
}
aResponse.setNull();
return;
}
if (!mResponseBlob) {
@ -1431,8 +1384,7 @@ XMLHttpRequestMainThread::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
mInLoadProgressEvent = false;
// clear chunked responses after every progress event
if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
mResponseBody.Truncate();
TruncateResponseText();
mResultArrayBuffer = nullptr;
@ -1715,13 +1667,6 @@ XMLHttpRequestMainThread::StreamReaderFunc(nsIInputStream* in,
if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob) {
xmlHttpRequest->MaybeCreateBlobStorage();
rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count);
} else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) {
if (!xmlHttpRequest->mBlobSet) {
xmlHttpRequest->mBlobSet = new BlobSet();
}
rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count);
// Clear the cache so that the blob size is updated.
xmlHttpRequest->mResponseBlob = nullptr;
} else if ((xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
!xmlHttpRequest->mIsMappedArrayBuffer) ||
xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
@ -1745,8 +1690,7 @@ XMLHttpRequestMainThread::StreamReaderFunc(nsIInputStream* in,
}
} else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::_empty ||
xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Text ||
xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Json ||
xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Json) {
NS_ASSERTION(!xmlHttpRequest->mResponseXML,
"We shouldn't be parsing a doc here");
rv = xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
@ -1885,7 +1829,6 @@ XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob)
mResponseBlob = aBlob;
mBlobStorage = nullptr;
mBlobSet = nullptr;
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
ChangeStateToDone();
@ -1908,8 +1851,7 @@ XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
nsresult rv;
nsCOMPtr<nsIFile> localFile;
if ((mResponseType == XMLHttpRequestResponseType::Blob ||
mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
if (mResponseType == XMLHttpRequestResponseType::Blob) {
rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -1917,7 +1859,6 @@ XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
if (localFile) {
mBlobStorage = nullptr;
mBlobSet = nullptr;
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
// The nsIStreamListener contract mandates us to read from the stream
@ -2271,8 +2212,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
// blob. We have this error because we canceled the channel. The status will
// be set to NS_OK.
if (status == NS_ERROR_FILE_ALREADY_EXISTS &&
(mResponseType == XMLHttpRequestResponseType::Blob ||
mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
mResponseType == XMLHttpRequestResponseType::Blob) {
nsCOMPtr<nsIFile> file;
nsresult rv = GetLocalFileFromChannel(request, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -2308,41 +2248,18 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
}
if (NS_SUCCEEDED(status) &&
(mResponseType == XMLHttpRequestResponseType::Blob ||
mResponseType == XMLHttpRequestResponseType::Moz_blob) &&
mResponseType == XMLHttpRequestResponseType::Blob &&
!waitingForBlobCreation) {
// Smaller files may be written in cache map instead of separate files.
// Also, no-store response cannot be written in persistent cache.
nsAutoCString contentType;
mChannel->GetContentType(contentType);
if (mResponseType == XMLHttpRequestResponseType::Blob) {
// mBlobStorage can be null if the channel is non-file non-cacheable
// and if the response length is zero.
MaybeCreateBlobStorage();
mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
waitingForBlobCreation = true;
} else {
// mBlobSet can be null if the channel is non-file non-cacheable
// and if the response length is zero.
if (!mBlobSet) {
mBlobSet = new BlobSet();
}
ErrorResult error;
nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
RefPtr<BlobImpl> blobImpl =
MultipartBlobImpl::Create(Move(subImpls),
NS_ConvertASCIItoUTF16(contentType),
error);
mBlobSet = nullptr;
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
mResponseBlob = Blob::Create(GetOwner(), blobImpl);
}
// mBlobStorage can be null if the channel is non-file non-cacheable
// and if the response length is zero.
MaybeCreateBlobStorage();
mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
waitingForBlobCreation = true;
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");

View File

@ -526,7 +526,6 @@ protected:
uint32_t count,
uint32_t *writeCount);
nsresult CreateResponseParsedJSON(JSContext* aCx);
void CreatePartialBlob(ErrorResult& aRv);
// Change the state of the object with this. The broadcast argument
// determines if the onreadystatechange listener should be called.
nsresult ChangeState(State aState, bool aBroadcast = true);
@ -666,8 +665,6 @@ protected:
RefPtr<Blob> mResponseBlob;
// We stream data to mBlobStorage when response type is "blob".
RefPtr<MutableBlobStorage> mBlobStorage;
// We stream data to mBlobSet when response type is "moz-blob".
nsAutoPtr<BlobSet> mBlobSet;
nsString mOverrideMimeType;

View File

@ -129,7 +129,6 @@ checkSetResponseType(xhr, "document");
checkSetResponseType(xhr, "arraybuffer");
checkSetResponseType(xhr, "blob");
checkSetResponseType(xhr, "json");
checkSetResponseType(xhr, "moz-chunked-text");
checkSetResponseType(xhr, "moz-chunked-arraybuffer");
checkOpenThrows(xhr, "GET", "file_XHR_pass2.txt", false);
@ -142,7 +141,6 @@ checkSetResponseTypeThrows(xhr, "document");
checkSetResponseTypeThrows(xhr, "arraybuffer");
checkSetResponseTypeThrows(xhr, "blob");
checkSetResponseTypeThrows(xhr, "json");
checkSetResponseTypeThrows(xhr, "moz-chunked-text");
checkSetResponseTypeThrows(xhr, "moz-chunked-arraybuffer");
xhr.send(null);
checkSetResponseTypeThrows(xhr, "document");
@ -276,92 +274,88 @@ while (xhr.readyState != 4) {
checkXHRStatus();
// test response (responseType='blob')
var responseTypes = ['blob', 'moz-blob'];
for (var i = 0; i < responseTypes.length; i++) {
var t = responseTypes[i];
// with a simple text file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_pass2.txt');
xhr.responseType = t;
xhr.onloadend = continueTest;
xhr.send(null);
yield undefined;
is(xhr.status, 200, "wrong status");
checkResponseTextAccessThrows(xhr);
checkResponseXMLAccessThrows(xhr);
var b = xhr.response;
ok(b, "should have a non-null blob");
ok(b instanceof Blob, "should be a Blob");
ok(!(b instanceof File), "should not be a File");
is(b.size, "hello pass\n".length, "wrong blob size");
// with a simple text file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_pass2.txt');
xhr.responseType = 'blob';
xhr.onloadend = continueTest;
xhr.send(null);
yield undefined;
is(xhr.status, 200, "wrong status");
checkResponseTextAccessThrows(xhr);
checkResponseXMLAccessThrows(xhr);
var b = xhr.response;
ok(b, "should have a non-null blob");
ok(b instanceof Blob, "should be a Blob");
ok(!(b instanceof File), "should not be a File");
is(b.size, "hello pass\n".length, "wrong blob size");
var fr = new FileReader();
fr.onload = continueTest;
fr.readAsBinaryString(b);
yield undefined;
ok(fr.result, "hello pass\n", "wrong values");
var fr = new FileReader();
fr.onload = continueTest;
fr.readAsBinaryString(b);
yield undefined;
ok(fr.result, "hello pass\n", "wrong values");
// with a binary file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_binary1.bin', true);
xhr.send(null);
xhr.onreadystatechange = continueTest;
while(xhr.readyState != 2)
yield undefined;
is(xhr.status, 200, "wrong status");
xhr.responseType = t;
while(xhr.readyState != 4)
yield undefined;
xhr.onreadystatechange = null;
b = xhr.response;
ok(b != null, "should have a non-null blob");
is(b.size, 12, "wrong blob size");
fr = new FileReader();
fr.readAsBinaryString(b);
xhr = null; // kill the XHR object
b = null;
SpecialPowers.gc();
fr.onload = continueTest;
yield undefined;
is(fr.result, "\xaa\xee\0\x03\xff\xff\xff\xff\xbb\xbb\xbb\xbb", "wrong values");
// with a larger binary file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_binary2.bin', true);
xhr.responseType = t;
xhr.send(null);
xhr.onreadystatechange = continueTest;
while (xhr.readyState != 4)
yield undefined;
xhr.onreadystatechange = null;
var b = xhr.response;
ok(b != null, "should have a non-null blob");
is(b.size, 65536, "wrong blob size");
fr = new FileReader();
fr.readAsArrayBuffer(b);
fr.onload = continueTest;
xhr = null; // kill the XHR object
b = null;
SpecialPowers.gc();
// with a binary file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_binary1.bin', true);
xhr.send(null);
xhr.onreadystatechange = continueTest;
while(xhr.readyState != 2)
yield undefined;
var u8 = new Uint8Array(fr.result);
for (var i = 0; i < 65536; i++) {
if (u8[i] !== (i & 255)) {
break;
}
is(xhr.status, 200, "wrong status");
xhr.responseType = 'blob';
while(xhr.readyState != 4)
yield undefined;
xhr.onreadystatechange = null;
b = xhr.response;
ok(b != null, "should have a non-null blob");
is(b.size, 12, "wrong blob size");
fr = new FileReader();
fr.readAsBinaryString(b);
xhr = null; // kill the XHR object
b = null;
SpecialPowers.gc();
fr.onload = continueTest;
yield undefined;
is(fr.result, "\xaa\xee\0\x03\xff\xff\xff\xff\xbb\xbb\xbb\xbb", "wrong values");
// with a larger binary file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_binary2.bin', true);
xhr.responseType = 'blob';
xhr.send(null);
xhr.onreadystatechange = continueTest;
while (xhr.readyState != 4)
yield undefined;
xhr.onreadystatechange = null;
var b = xhr.response;
ok(b != null, "should have a non-null blob");
is(b.size, 65536, "wrong blob size");
fr = new FileReader();
fr.readAsArrayBuffer(b);
fr.onload = continueTest;
xhr = null; // kill the XHR object
b = null;
SpecialPowers.gc();
yield undefined;
var u8 = new Uint8Array(fr.result);
for (var i = 0; i < 65536; i++) {
if (u8[i] !== (i & 255)) {
break;
}
is(i, 65536, "wrong value at offset " + i);
}
is(i, 65536, "wrong value at offset " + i);
var client = new XMLHttpRequest();
client.open("GET", "file_XHR_pass1.xml", true);

View File

@ -74,26 +74,11 @@ function testNonParsingText() {
if (this.readyState == 4) {
is(this.responseText.indexOf("\u042E"), -1, "Honored meta in text mode.");
is(this.responseText.indexOf("\uFFFD"), 29, "Honored meta in text mode 2.");
testChunkedText();
}
}
xhr.open("GET", "file_html_in_xhr2.html");
xhr.responseType = "text";
xhr.send();
}
function testChunkedText() {
xhr = new XMLHttpRequest();
xhr.onprogress = function() {
is(this.responseText.indexOf("\u042E"), -1, "Honored meta in chunked text mode.");
}
xhr.onreadystatechange = function() {
if (this.readyState == 4) {
testSyncXHR();
}
}
xhr.open("GET", "file_html_in_xhr2.html");
xhr.responseType = "moz-chunked-text";
xhr.responseType = "text";
xhr.send();
}

View File

@ -132,11 +132,9 @@ function* runTests() {
var responseTypes = [{ type: "text", text: true },
{ type: "arraybuffer", text: false, nodata: true },
{ type: "blob", text: false, nodata: true, blob: true },
{ type: "moz-blob", text: false, nodata: false, blob: true },
{ type: "document", text: true, nodata: true },
{ type: "json", text: true, nodata: true },
{ type: "", text: true },
{ type: "moz-chunked-text", text: true, chunked: true },
{ type: "moz-chunked-arraybuffer", text: false, chunked: true },
];
var responseType;

View File

@ -96,68 +96,5 @@ onmessage = function(event) {
throw new Error("'document' type not working correctly");
}
// Make sure setting responseType before open or after send fails.
var exception;
xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "text";
xhr.onload = function(event) {
if (event.target.response != refText) {
throw new Error("Bad response!");
}
xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "moz-chunked-text";
var lastIndex = 0;
xhr.onprogress = function(event) {
if (refText.substr(lastIndex, xhr.response.length) != xhr.response) {
throw new Error("Bad chunk!");
}
lastIndex += xhr.response.length;
};
xhr.onload = function(event) {
if (lastIndex != refText.length) {
throw new Error("Didn't see all the data!");
}
setTimeout(function() {
if (xhr.response !== null) {
throw new Error("Should have gotten null response outside of event!");
}
postMessage("done");
}, 0);
}
xhr.send(null);
};
xhr.send();
exception = null;
try {
xhr.responseType = "arraybuffer";
}
catch(e) {
exception = e;
}
if (!exception) {
throw new Error("Failed to throw when setting responseType after " +
"calling send()");
}
if (exception.name != "InvalidStateError") {
throw new Error("Unexpected error when setting responseType after " +
"calling send()");
}
if (exception.code != DOMException.INVALID_STATE_ERR) {
throw new Error("Unexpected error code when setting responseType after " +
"calling send()");
}
postMessage("done");
}

View File

@ -227,6 +227,13 @@ DrawTargetCaptureImpl::PushLayer(bool aOpaque,
const IntRect& aBounds,
bool aCopyBackground)
{
// Have to update mPermitSubpixelAA for this DT
// because some code paths query the current setting
// to determine subpixel AA eligibility.
PushedLayer layer(GetPermitSubpixelAA());
mPushedLayers.push_back(layer);
DrawTarget::SetPermitSubpixelAA(aOpaque);
AppendCommand(PushLayerCommand)(aOpaque,
aOpacity,
aMask,
@ -238,6 +245,10 @@ DrawTargetCaptureImpl::PushLayer(bool aOpaque,
void
DrawTargetCaptureImpl::PopLayer()
{
MOZ_ASSERT(mPushedLayers.size());
DrawTarget::SetPermitSubpixelAA(mPushedLayers.back().mOldPermitSubpixelAA);
mPushedLayers.pop_back();
AppendCommand(PopLayerCommand)();
}

View File

@ -168,6 +168,14 @@ private:
RefPtr<DrawTarget> mRefDT;
IntSize mSize;
struct PushedLayer
{
explicit PushedLayer(bool aOldPermitSubpixelAA)
: mOldPermitSubpixelAA(aOldPermitSubpixelAA)
{}
bool mOldPermitSubpixelAA;
};
std::vector<PushedLayer> mPushedLayers;
std::vector<uint8_t> mDrawCommandStorage;
};

View File

@ -12,20 +12,14 @@
#include "mozilla/Sprintf.h"
#include "nsUnicodeProperties.h"
#include "nsUnicodeScriptCodes.h"
#include "nsUnicodeNormalizer.h"
#include "harfbuzz/hb.h"
#include "harfbuzz/hb-ot.h"
#if ENABLE_INTL_API // ICU is available: we'll use it for Unicode composition
// and decomposition in preference to nsUnicodeNormalizer.
#include "unicode/unorm.h"
#include "unicode/utext.h"
#define MOZ_HB_SHAPER_USE_ICU_NORMALIZATION 1
static const UNormalizer2 * sNormalizer = nullptr;
#else
#undef MOZ_HB_SHAPER_USE_ICU_NORMALIZATION
#endif
static const UNormalizer2* sNormalizer = nullptr;
#include <algorithm>
@ -1106,8 +1100,6 @@ HBUnicodeCompose(hb_unicode_funcs_t *ufuncs,
hb_codepoint_t *ab,
void *user_data)
{
#if MOZ_HB_SHAPER_USE_ICU_NORMALIZATION
if (sNormalizer) {
UChar32 ch = unorm2_composePair(sNormalizer, a, b);
if (ch >= 0) {
@ -1116,14 +1108,6 @@ HBUnicodeCompose(hb_unicode_funcs_t *ufuncs,
}
}
#else // no ICU available, use the old nsUnicodeNormalizer
if (nsUnicodeNormalizer::Compose(a, b, ab)) {
return true;
}
#endif
return false;
}
@ -1144,8 +1128,6 @@ HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs,
}
#endif
#if MOZ_HB_SHAPER_USE_ICU_NORMALIZATION
if (!sNormalizer) {
return false;
}
@ -1177,12 +1159,6 @@ HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs,
utext_close(&text);
return *b != 0 || *a != ab;
#else // no ICU available, use the old nsUnicodeNormalizer
return nsUnicodeNormalizer::DecomposeNonRecursively(ab, a, b);
#endif
}
static void
@ -1265,11 +1241,9 @@ gfxHarfBuzzShaper::Initialize()
HBUnicodeDecompose,
nullptr, nullptr);
#if MOZ_HB_SHAPER_USE_ICU_NORMALIZATION
UErrorCode error = U_ZERO_ERROR;
sNormalizer = unorm2_getNFCInstance(&error);
NS_ASSERTION(U_SUCCESS(error), "failed to get ICU normalizer");
#endif
MOZ_ASSERT(U_SUCCESS(error), "failed to get ICU normalizer");
}
gfxFontEntry *entry = mFont->GetFontEntry();

View File

@ -233,12 +233,10 @@ if CONFIG['MOZ_ENABLE_SKIA_PDF']:
'PrintTargetSkPDF.cpp',
]
# We prefer to use ICU for normalization functions, but currently it is only
# available if we're building with the Intl API enabled:
if CONFIG['ENABLE_INTL_API']:
USE_LIBS += [
'icu',
]
# We use ICU for normalization functions:
USE_LIBS += [
'icu',
]
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -18,14 +18,9 @@ EXPORTS += [
'nsUnicodeNormalizer.h',
]
if CONFIG['ENABLE_INTL_API']:
UNIFIED_SOURCES += [
'nsUnicodeNormalizer_ICU.cpp',
]
else:
UNIFIED_SOURCES += [
'nsUnicodeNormalizer.cpp',
]
UNIFIED_SOURCES += [
'nsUnicodeNormalizer.cpp',
]
FINAL_LIBRARY = 'xul'

File diff suppressed because it is too large Load Diff

View File

@ -1,704 +1,91 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This file is modified from JPNIC's mDNKit, it is under both MPL and
* JPNIC's license.
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Copyright (c) 2000,2002 Japan Network Information Center.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set forth bellow.
*
* LICENSE TERMS AND CONDITIONS
*
* The following License Terms and Conditions apply, unless a different
* license is obtained from Japan Network Information Center ("JPNIC"),
* a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
* Chiyoda-ku, Tokyo 101-0047, Japan.
*
* 1. Use, Modification and Redistribution (including distribution of any
* modified or derived work) in source and/or binary forms is permitted
* under this License Terms and Conditions.
*
* 2. Redistribution of source code must retain the copyright notices as they
* appear in each source code file, this License Terms and Conditions.
*
* 3. Redistribution in binary form must reproduce the Copyright Notice,
* this License Terms and Conditions, in the documentation and/or other
* materials provided with the distribution. For the purposes of binary
* distribution the "Copyright Notice" refers to the following language:
* "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
*
* 4. The name of JPNIC may not be used to endorse or promote products
* derived from this Software without specific prior written approval of
* JPNIC.
*
* 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
#include <string.h>
#include "nsMemory.h"
#include "nsUnicodeNormalizer.h"
#include "nsString.h"
#include "mozilla/BinarySearch.h"
#include "ICUUtils.h"
#include "unicode/unorm2.h"
#include "unicode/utext.h"
NS_IMPL_ISUPPORTS(nsUnicodeNormalizer, nsIUnicodeNormalizer)
nsUnicodeNormalizer::nsUnicodeNormalizer()
{
}
nsUnicodeNormalizer::~nsUnicodeNormalizer()
{
}
#define END_BIT 0x80000000
/*
* Some constants for Hangul decomposition/composition.
* These things were taken from unicode book.
*/
#define SBase 0xac00
#define LBase 0x1100
#define VBase 0x1161
#define TBase 0x11a7
#define LCount 19
#define VCount 21
#define TCount 28
#define SLast (SBase + LCount * VCount * TCount)
struct composition {
uint32_t c2; /* 2nd character */
uint32_t comp; /* composed character */
};
#include "normalization_data.h"
/*
* Macro for multi-level index table.
*/
#define LOOKUPTBL(vprefix, mprefix, v) \
DMAP(vprefix)[\
IMAP(vprefix)[\
IMAP(vprefix)[IDX0(mprefix, v)] + IDX1(mprefix, v)\
]\
].tbl[IDX2(mprefix, v)]
#define IDX0(mprefix, v) IDX_0(v, BITS1(mprefix), BITS2(mprefix))
#define IDX1(mprefix, v) IDX_1(v, BITS1(mprefix), BITS2(mprefix))
#define IDX2(mprefix, v) IDX_2(v, BITS1(mprefix), BITS2(mprefix))
#define IDX_0(v, bits1, bits2) ((v) >> ((bits1) + (bits2)))
#define IDX_1(v, bits1, bits2) (((v) >> (bits2)) & ((1 << (bits1)) - 1))
#define IDX_2(v, bits1, bits2) ((v) & ((1 << (bits2)) - 1))
#define BITS1(mprefix) mprefix ## _BITS_1
#define BITS2(mprefix) mprefix ## _BITS_2
#define IMAP(vprefix) vprefix ## _imap
#define DMAP(vprefix) vprefix ## _table
#define SEQ(vprefix) vprefix ## _seq
static int32_t
canonclass(uint32_t c) {
/* Look up canonicalclass table. */
return (LOOKUPTBL(canon_class, CANON_CLASS, c));
}
static int32_t
decompose_char(uint32_t c, const uint32_t **seqp)
{
/* Look up decomposition table. */
int32_t seqidx = LOOKUPTBL(decompose, DECOMP, c);
*seqp = SEQ(decompose) + (seqidx & ~DECOMP_COMPAT);
return (seqidx);
}
static int32_t
compose_char(uint32_t c,
const struct composition **compp)
{
/* Look up composition table. */
int32_t seqidx = LOOKUPTBL(compose, CANON_COMPOSE, c);
*compp = SEQ(compose) + (seqidx & 0xffff);
return (seqidx >> 16);
}
static nsresult
mdn__unicode_decompose(int32_t compat, uint32_t *v, size_t vlen,
uint32_t c, int32_t *decomp_lenp)
DoNormalization(const UNormalizer2* aNorm, const nsAString& aSrc,
nsAString& aDest)
{
uint32_t *vorg = v;
int32_t seqidx;
const uint32_t *seq;
//assert(v != nullptr && vlen >= 0 && decomp_lenp != nullptr);
/*
* First, check for Hangul.
*/
if (SBase <= c && c < SLast) {
int32_t idx, t_offset, v_offset, l_offset;
idx = c - SBase;
t_offset = idx % TCount;
idx /= TCount;
v_offset = idx % VCount;
l_offset = idx / VCount;
if ((t_offset == 0 && vlen < 2) || (t_offset > 0 && vlen < 3))
return (NS_ERROR_UNORM_MOREOUTPUT);
*v++ = LBase + l_offset;
*v++ = VBase + v_offset;
if (t_offset > 0)
*v++ = TBase + t_offset;
*decomp_lenp = v - vorg;
return (NS_OK);
}
/*
* Look up decomposition table. If no decomposition is defined
* or if it is a compatibility decomosition when canonical
* decomposition requested, return 'NS_SUCCESS_UNORM_NOTFOUND'.
*/
seqidx = decompose_char(c, &seq);
if (seqidx == 0 || (compat == 0 && (seqidx & DECOMP_COMPAT) != 0))
return (NS_SUCCESS_UNORM_NOTFOUND);
/*
* Copy the decomposed sequence. The end of the sequence are
* marked with END_BIT.
*/
do {
uint32_t c;
int32_t dlen;
nsresult r;
c = *seq & ~END_BIT;
/* Decompose recursively. */
r = mdn__unicode_decompose(compat, v, vlen, c, &dlen);
if (r == NS_OK) {
v += dlen;
vlen -= dlen;
} else if (r == NS_SUCCESS_UNORM_NOTFOUND) {
if (vlen < 1)
return (NS_ERROR_UNORM_MOREOUTPUT);
*v++ = c;
vlen--;
} else {
return (r);
}
} while ((*seq++ & END_BIT) == 0);
*decomp_lenp = v - vorg;
return (NS_OK);
}
static int32_t
mdn__unicode_iscompositecandidate(uint32_t c)
{
const struct composition *dummy;
/* Check for Hangul */
if ((LBase <= c && c < LBase + LCount) || (SBase <= c && c < SLast))
return (1);
/*
* Look up composition table. If there are no composition
* that begins with the given character, it is not a
* composition candidate.
*/
if (compose_char(c, &dummy) == 0)
return (0);
else
return (1);
}
namespace {
struct SequenceAdaptor
{
const composition* const mSequence;
explicit SequenceAdaptor(const composition* aSequence) : mSequence(aSequence) {}
uint32_t operator[](size_t aIdx) const {
return mSequence[aIdx].c2;
}
};
} // namespace
static nsresult
mdn__unicode_compose(uint32_t c1, uint32_t c2, uint32_t *compp)
{
int32_t n;
const struct composition *cseq;
//assert(compp != nullptr);
/*
* Check for Hangul.
*/
if (LBase <= c1 && c1 < LBase + LCount &&
VBase <= c2 && c2 < VBase + VCount) {
/*
* Hangul L and V.
*/
*compp = SBase +
((c1 - LBase) * VCount + (c2 - VBase)) * TCount;
return (NS_OK);
} else if (SBase <= c1 && c1 < SLast &&
TBase <= c2 && c2 < TBase + TCount &&
(c1 - SBase) % TCount == 0) {
/*
* Hangul LV and T.
*/
*compp = c1 + (c2 - TBase);
return (NS_OK);
}
/*
* Look up composition table. If the result is 0, no composition
* is defined. Otherwise, upper 16bits of the result contains
* the number of composition that begins with 'c1', and the lower
* 16bits is the offset in 'compose_seq'.
*/
if ((n = compose_char(c1, &cseq)) == 0)
return (NS_SUCCESS_UNORM_NOTFOUND);
/*
* The composite sequences are sorted by the 2nd character 'c2'.
* So we can use binary search.
*/
size_t idx;
if (mozilla::BinarySearch(SequenceAdaptor(cseq), 0, n, c2, &idx)) {
*compp = cseq[idx].comp;
return (NS_OK);
}
return (NS_SUCCESS_UNORM_NOTFOUND);
}
#define WORKBUF_SIZE 128
#define WORKBUF_SIZE_MAX 10000
typedef struct {
int32_t cur; /* pointing now processing character */
int32_t last; /* pointing just after the last character */
int32_t size; /* size of UCS and CLASS array */
uint32_t *ucs; /* UCS-4 characters */
int32_t *cclass; /* and their canonical classes */
uint32_t ucs_buf[WORKBUF_SIZE]; /* local buffer */
int32_t class_buf[WORKBUF_SIZE]; /* ditto */
} workbuf_t;
static nsresult decompose(workbuf_t *wb, uint32_t c, int32_t compat);
static void get_class(workbuf_t *wb);
static void reorder(workbuf_t *wb);
static void compose(workbuf_t *wb);
static nsresult flush_before_cur(workbuf_t *wb, nsAString& aToStr);
static void workbuf_init(workbuf_t *wb);
static void workbuf_free(workbuf_t *wb);
static nsresult workbuf_extend(workbuf_t *wb);
static nsresult workbuf_append(workbuf_t *wb, uint32_t c);
static void workbuf_shift(workbuf_t *wb, int32_t shift);
static void workbuf_removevoid(workbuf_t *wb);
static nsresult
mdn_normalize(bool do_composition, bool compat,
const nsAString& aSrcStr, nsAString& aToStr)
{
workbuf_t wb;
nsresult r = NS_OK;
/*
* Initialize working buffer.
*/
workbuf_init(&wb);
nsAString::const_iterator start, end;
aSrcStr.BeginReading(start);
aSrcStr.EndReading(end);
while (start != end) {
uint32_t c;
char16_t curChar;
//assert(wb.cur == wb.last);
/*
* Get one character from 'from'.
*/
curChar= *start++;
if (NS_IS_HIGH_SURROGATE(curChar) && start != end && NS_IS_LOW_SURROGATE(*(start)) ) {
c = SURROGATE_TO_UCS4(curChar, *start);
++start;
} else {
c = curChar;
}
/*
* Decompose it.
*/
if ((r = decompose(&wb, c, compat)) != NS_OK)
break;
/*
* Get canonical class.
*/
get_class(&wb);
/*
* Reorder & compose.
*/
for (; wb.cur < wb.last; wb.cur++) {
if (wb.cur == 0) {
continue;
} else if (wb.cclass[wb.cur] > 0) {
/*
* This is not a starter. Try reordering.
* Note that characters up to it are
* already in canonical order.
*/
reorder(&wb);
continue;
}
/*
* This is a starter character, and there are
* some characters before it. Those characters
* have been reordered properly, and
* ready for composition.
*/
if (do_composition && wb.cclass[0] == 0)
compose(&wb);
/*
* If CUR points to a starter character,
* then process of characters before CUR are
* already finished, because any further
* reordering/composition for them are blocked
* by the starter CUR points.
*/
if (wb.cur > 0 && wb.cclass[wb.cur] == 0) {
/* Flush everything before CUR. */
r = flush_before_cur(&wb, aToStr);
if (r != NS_OK)
break;
}
}
}
if (r == NS_OK) {
if (do_composition && wb.cur > 0 && wb.cclass[0] == 0) {
/*
* There is some characters left in WB.
* They are ordered, but not composed yet.
* Now CUR points just after the last character in WB,
* and since compose() tries to compose characters
* between top and CUR inclusive, we must make CUR
* one character back during compose().
*/
wb.cur--;
compose(&wb);
wb.cur++;
}
/*
* Call this even when WB.CUR == 0, to make TO
* NUL-terminated.
*/
r = flush_before_cur(&wb, aToStr);
}
workbuf_free(&wb);
return (r);
}
static nsresult
decompose(workbuf_t *wb, uint32_t c, int32_t compat) {
nsresult r;
int32_t dec_len;
again:
r = mdn__unicode_decompose(compat, wb->ucs + wb->last,
wb->size - wb->last, c, &dec_len);
switch (r) {
case NS_OK:
wb->last += dec_len;
return (NS_OK);
case NS_SUCCESS_UNORM_NOTFOUND:
return (workbuf_append(wb, c));
case NS_ERROR_UNORM_MOREOUTPUT:
if ((r = workbuf_extend(wb)) != NS_OK)
return (r);
if (wb->size > WORKBUF_SIZE_MAX) {
// "mdn__unormalize_form*: " "working buffer too large\n"
return (NS_ERROR_FAILURE);
}
goto again;
default:
return (r);
}
/* NOTREACHED */
}
static void
get_class(workbuf_t *wb) {
int32_t i;
for (i = wb->cur; i < wb->last; i++)
wb->cclass[i] = canonclass(wb->ucs[i]);
}
static void
reorder(workbuf_t *wb) {
uint32_t c;
int32_t i;
int32_t cclass;
//assert(wb != nullptr);
i = wb->cur;
c = wb->ucs[i];
cclass = wb->cclass[i];
while (i > 0 && wb->cclass[i - 1] > cclass) {
wb->ucs[i] = wb->ucs[i - 1];
wb->cclass[i] =wb->cclass[i - 1];
i--;
wb->ucs[i] = c;
wb->cclass[i] = cclass;
}
}
static void
compose(workbuf_t *wb) {
int32_t cur;
uint32_t *ucs;
int32_t *cclass;
int32_t last_class;
int32_t nvoids;
int32_t i;
//assert(wb != nullptr && wb->cclass[0] == 0);
cur = wb->cur;
ucs = wb->ucs;
cclass = wb->cclass;
/*
* If there are no decomposition sequence that begins with
* the top character, composition is impossible.
*/
if (!mdn__unicode_iscompositecandidate(ucs[0]))
return;
last_class = 0;
nvoids = 0;
for (i = 1; i <= cur; i++) {
uint32_t c;
int32_t cl = cclass[i];
if ((last_class < cl || cl == 0) &&
mdn__unicode_compose(ucs[0], ucs[i],
&c) == NS_OK) {
/*
* Replace the top character with the composed one.
*/
ucs[0] = c;
cclass[0] = canonclass(c);
cclass[i] = -1; /* void this character */
nvoids++;
} else {
last_class = cl;
}
}
/* Purge void characters, if any. */
if (nvoids > 0)
workbuf_removevoid(wb);
}
static nsresult
flush_before_cur(workbuf_t *wb, nsAString& aToStr)
{
int32_t i;
for (i = 0; i < wb->cur; i++) {
if (!IS_IN_BMP(wb->ucs[i])) {
aToStr.Append((char16_t)H_SURROGATE(wb->ucs[i]));
aToStr.Append((char16_t)L_SURROGATE(wb->ucs[i]));
} else {
aToStr.Append((char16_t)(wb->ucs[i]));
}
}
workbuf_shift(wb, wb->cur);
return (NS_OK);
}
static void
workbuf_init(workbuf_t *wb) {
wb->cur = 0;
wb->last = 0;
wb->size = WORKBUF_SIZE;
wb->ucs = wb->ucs_buf;
wb->cclass = wb->class_buf;
}
static void
workbuf_free(workbuf_t *wb) {
if (wb->ucs != wb->ucs_buf) {
free(wb->ucs);
free(wb->cclass);
}
}
static nsresult
workbuf_extend(workbuf_t *wb) {
int32_t newsize = wb->size * 3;
if (wb->ucs == wb->ucs_buf) {
wb->ucs = (uint32_t*)moz_xmalloc(sizeof(wb->ucs[0]) * newsize);
if (!wb->ucs)
return NS_ERROR_OUT_OF_MEMORY;
wb->cclass = (int32_t*)moz_xmalloc(sizeof(wb->cclass[0]) * newsize);
if (!wb->cclass) {
free(wb->ucs);
wb->ucs = nullptr;
return NS_ERROR_OUT_OF_MEMORY;
}
} else {
void* buf = moz_xrealloc(wb->ucs, sizeof(wb->ucs[0]) * newsize);
if (!buf)
return NS_ERROR_OUT_OF_MEMORY;
wb->ucs = (uint32_t*)buf;
buf = moz_xrealloc(wb->cclass, sizeof(wb->cclass[0]) * newsize);
if (!buf)
return NS_ERROR_OUT_OF_MEMORY;
wb->cclass = (int32_t*)buf;
}
return (NS_OK);
}
static nsresult
workbuf_append(workbuf_t *wb, uint32_t c) {
nsresult r;
if (wb->last >= wb->size && (r = workbuf_extend(wb)) != NS_OK)
return (r);
wb->ucs[wb->last++] = c;
return (NS_OK);
}
static void
workbuf_shift(workbuf_t *wb, int32_t shift) {
int32_t nmove;
//assert(wb != nullptr && wb->cur >= shift);
nmove = wb->last - shift;
memmove(&wb->ucs[0], &wb->ucs[shift],
nmove * sizeof(wb->ucs[0]));
memmove(&wb->cclass[0], &wb->cclass[shift],
nmove * sizeof(wb->cclass[0]));
wb->cur -= shift;
wb->last -= shift;
}
static void
workbuf_removevoid(workbuf_t *wb) {
int32_t i, j;
int32_t last = wb->last;
for (i = j = 0; i < last; i++) {
if (wb->cclass[i] >= 0) {
if (j < i) {
wb->ucs[j] = wb->ucs[i];
wb->cclass[j] = wb->cclass[i];
}
j++;
}
}
wb->cur -= last - j;
wb->last = j;
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFD( const nsAString& aSrc, nsAString& aDest)
{
return mdn_normalize(false, false, aSrc, aDest);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFC( const nsAString& aSrc, nsAString& aDest)
{
return mdn_normalize(true, false, aSrc, aDest);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFKD( const nsAString& aSrc, nsAString& aDest)
{
return mdn_normalize(false, true, aSrc, aDest);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFKC( const nsAString& aSrc, nsAString& aDest)
{
return mdn_normalize(true, true, aSrc, aDest);
}
bool
nsUnicodeNormalizer::Compose(uint32_t a, uint32_t b, uint32_t *ab)
{
return mdn__unicode_compose(a, b, ab) == NS_OK;
}
bool
nsUnicodeNormalizer::DecomposeNonRecursively(uint32_t c, uint32_t *c1, uint32_t *c2)
{
// We can't use mdn__unicode_decompose here, because that does a recursive
// decomposition that may yield more than two characters, but the harfbuzz
// callback wants just a single-step decomp that is guaranteed to produce
// no more than two characters. So we do a low-level lookup in the table
// of decomp sequences.
const uint32_t *seq;
uint32_t seqidx = decompose_char(c, &seq);
if (seqidx == 0 || ((seqidx & DECOMP_COMPAT) != 0)) {
return false;
UErrorCode errorCode = U_ZERO_ERROR;
const int32_t length = aSrc.Length();
const UChar* src = reinterpret_cast<const UChar*>(aSrc.BeginReading());
// Initial guess for a capacity that is likely to be enough for most cases.
int32_t capacity = length + (length >> 8) + 8;
while (true) {
aDest.SetLength(capacity);
UChar* dest = reinterpret_cast<UChar*>(aDest.BeginWriting());
int32_t len = unorm2_normalize(aNorm, src, aSrc.Length(), dest, capacity,
&errorCode);
if (U_SUCCESS(errorCode)) {
aDest.SetLength(len);
break;
}
if (errorCode != U_BUFFER_OVERFLOW_ERROR) {
// Some other error that we don't handle
break;
}
// Buffer wasn't big enough; adjust to the reported size and try again.
capacity = len;
errorCode = U_ZERO_ERROR;
}
*c1 = *seq & ~END_BIT;
if (*seq & END_BIT) {
*c2 = 0;
} else {
*c2 = *++seq & ~END_BIT;
}
return true;
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFD(const nsAString& aSrc,
nsAString& aDest)
{
// The unorm2_getNF*Instance functions return static singletons that should
// not be deleted, so we just get them once on first use.
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFDInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFC(const nsAString& aSrc,
nsAString& aDest)
{
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFCInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFKD(const nsAString& aSrc,
nsAString& aDest)
{
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFKDInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFKC(const nsAString& aSrc,
nsAString& aDest)
{
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFKCInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}

View File

@ -16,7 +16,7 @@ nsresult NS_NewUnicodeNormalizer(nsISupports** oResult);
class nsUnicodeNormalizer : public nsIUnicodeNormalizer {
public:
nsUnicodeNormalizer();
nsUnicodeNormalizer() { }
NS_DECL_ISUPPORTS
@ -25,15 +25,8 @@ public:
NS_IMETHOD NormalizeUnicodeNFKD( const nsAString& aSrc, nsAString& aDest) override;
NS_IMETHOD NormalizeUnicodeNFKC( const nsAString& aSrc, nsAString& aDest) override;
#if !ENABLE_INTL_API
// Low-level access to the composition data needed for HarfBuzz callbacks;
// only required when ICU is not available in the build.
static bool Compose(uint32_t a, uint32_t b, uint32_t *ab);
static bool DecomposeNonRecursively(uint32_t comp, uint32_t *c1, uint32_t *c2);
#endif
private:
virtual ~nsUnicodeNormalizer();
virtual ~nsUnicodeNormalizer() { }
};
#endif //nsUnicodeNormalizer_h__

View File

@ -1,99 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsUnicodeNormalizer.h"
#include "ICUUtils.h"
#include "unicode/unorm2.h"
#include "unicode/utext.h"
NS_IMPL_ISUPPORTS(nsUnicodeNormalizer, nsIUnicodeNormalizer)
nsUnicodeNormalizer::nsUnicodeNormalizer()
{
}
nsUnicodeNormalizer::~nsUnicodeNormalizer()
{
}
static nsresult
DoNormalization(const UNormalizer2* aNorm, const nsAString& aSrc,
nsAString& aDest)
{
UErrorCode errorCode = U_ZERO_ERROR;
const int32_t length = aSrc.Length();
const UChar* src = reinterpret_cast<const UChar*>(aSrc.BeginReading());
// Initial guess for a capacity that is likely to be enough for most cases.
int32_t capacity = length + (length >> 8) + 8;
while (true) {
aDest.SetLength(capacity);
UChar* dest = reinterpret_cast<UChar*>(aDest.BeginWriting());
int32_t len = unorm2_normalize(aNorm, src, aSrc.Length(), dest, capacity,
&errorCode);
if (U_SUCCESS(errorCode)) {
aDest.SetLength(len);
break;
}
if (errorCode != U_BUFFER_OVERFLOW_ERROR) {
// Some other error that we don't handle
break;
}
// Buffer wasn't big enough; adjust to the reported size and try again.
capacity = len;
errorCode = U_ZERO_ERROR;
}
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFD(const nsAString& aSrc,
nsAString& aDest)
{
// The unorm2_getNF*Instance functions return static singletons that should
// not be deleted, so we just get them once on first use.
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFDInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFC(const nsAString& aSrc,
nsAString& aDest)
{
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFCInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFKD(const nsAString& aSrc,
nsAString& aDest)
{
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFKDInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}
nsresult
nsUnicodeNormalizer::NormalizeUnicodeNFKC(const nsAString& aSrc,
nsAString& aDest)
{
static UErrorCode errorCode = U_ZERO_ERROR;
static const UNormalizer2* norm = unorm2_getNFKCInstance(&errorCode);
if (U_SUCCESS(errorCode)) {
return DoNormalization(norm, aSrc, aDest);
}
return ICUUtils::UErrorToNsResult(errorCode);
}

View File

@ -1,208 +0,0 @@
#
# $Id: MUTTUCData.txt,v 1.1 1999/01/08 00:19:19 ftang%netscape.com Exp $
#
# Copyright 1996, 1997, 1998 Computing Research Labs,
# New Mexico State University
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
# THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
#
# Implementation specific character properties.
#
#
# Space, other.
#
0009;;Ss;;;;;;;;;;;;
000A;;Ss;;;;;;;;;;;;
000B;;Ss;;;;;;;;;;;;
000C;;Ss;;;;;;;;;;;;
000D;;Ss;;;;;;;;;;;;
#
# Non-breaking.
#
00A0;;Nb;;;;;;;;;;;;
2007;;Nb;;;;;;;;;;;;
2011;;Nb;;;;;;;;;;;;
FEFF;;Nb;;;;;;;;;;;;
#
# Symmetric.
#
0028;;Sy;;;;;;;;;;;;
0029;;Sy;;;;;;;;;;;;
005B;;Sy;;;;;;;;;;;;
005D;;Sy;;;;;;;;;;;;
007B;;Sy;;;;;;;;;;;;
007D;;Sy;;;;;;;;;;;;
00AB;;Sy;;;;;;;;;;;;
00BB;;Sy;;;;;;;;;;;;
0F3A;;Sy;;;;;;;;;;;;
0F3B;;Sy;;;;;;;;;;;;
0F3C;;Sy;;;;;;;;;;;;
0F3D;;Sy;;;;;;;;;;;;
0F3E;;Sy;;;;;;;;;;;;
0F3F;;Sy;;;;;;;;;;;;
2018;;Sy;;;;;;;;;;;;
2019;;Sy;;;;;;;;;;;;
201A;;Sy;;;;;;;;;;;;
201B;;Sy;;;;;;;;;;;;
201C;;Sy;;;;;;;;;;;;
201D;;Sy;;;;;;;;;;;;
201E;;Sy;;;;;;;;;;;;
201F;;Sy;;;;;;;;;;;;
2039;;Sy;;;;;;;;;;;;
203A;;Sy;;;;;;;;;;;;
2045;;Sy;;;;;;;;;;;;
2046;;Sy;;;;;;;;;;;;
207D;;Sy;;;;;;;;;;;;
207E;;Sy;;;;;;;;;;;;
208D;;Sy;;;;;;;;;;;;
208E;;Sy;;;;;;;;;;;;
2329;;Sy;;;;;;;;;;;;
232A;;Sy;;;;;;;;;;;;
3008;;Sy;;;;;;;;;;;;
3009;;Sy;;;;;;;;;;;;
300A;;Sy;;;;;;;;;;;;
300B;;Sy;;;;;;;;;;;;
300C;;Sy;;;;;;;;;;;;
300D;;Sy;;;;;;;;;;;;
300E;;Sy;;;;;;;;;;;;
300F;;Sy;;;;;;;;;;;;
3010;;Sy;;;;;;;;;;;;
3011;;Sy;;;;;;;;;;;;
3014;;Sy;;;;;;;;;;;;
3015;;Sy;;;;;;;;;;;;
3016;;Sy;;;;;;;;;;;;
3017;;Sy;;;;;;;;;;;;
3018;;Sy;;;;;;;;;;;;
3019;;Sy;;;;;;;;;;;;
301A;;Sy;;;;;;;;;;;;
301B;;Sy;;;;;;;;;;;;
301D;;Sy;;;;;;;;;;;;
301E;;Sy;;;;;;;;;;;;
FD3E;;Sy;;;;;;;;;;;;
FD3F;;Sy;;;;;;;;;;;;
FE35;;Sy;;;;;;;;;;;;
FE36;;Sy;;;;;;;;;;;;
FE37;;Sy;;;;;;;;;;;;
FE38;;Sy;;;;;;;;;;;;
FE39;;Sy;;;;;;;;;;;;
FE3A;;Sy;;;;;;;;;;;;
FE3B;;Sy;;;;;;;;;;;;
FE3C;;Sy;;;;;;;;;;;;
FE3D;;Sy;;;;;;;;;;;;
FE3E;;Sy;;;;;;;;;;;;
FE3F;;Sy;;;;;;;;;;;;
FE40;;Sy;;;;;;;;;;;;
FE41;;Sy;;;;;;;;;;;;
FE42;;Sy;;;;;;;;;;;;
FE43;;Sy;;;;;;;;;;;;
FE44;;Sy;;;;;;;;;;;;
FE59;;Sy;;;;;;;;;;;;
FE5A;;Sy;;;;;;;;;;;;
FE5B;;Sy;;;;;;;;;;;;
FE5C;;Sy;;;;;;;;;;;;
FE5D;;Sy;;;;;;;;;;;;
FE5E;;Sy;;;;;;;;;;;;
FF08;;Sy;;;;;;;;;;;;
FF09;;Sy;;;;;;;;;;;;
FF3B;;Sy;;;;;;;;;;;;
FF3D;;Sy;;;;;;;;;;;;
FF5B;;Sy;;;;;;;;;;;;
FF5D;;Sy;;;;;;;;;;;;
FF62;;Sy;;;;;;;;;;;;
FF63;;Sy;;;;;;;;;;;;
#
# Hex digit.
#
0030;;Hd;;;;;;;;;;;;
0031;;Hd;;;;;;;;;;;;
0032;;Hd;;;;;;;;;;;;
0033;;Hd;;;;;;;;;;;;
0034;;Hd;;;;;;;;;;;;
0035;;Hd;;;;;;;;;;;;
0036;;Hd;;;;;;;;;;;;
0037;;Hd;;;;;;;;;;;;
0038;;Hd;;;;;;;;;;;;
0039;;Hd;;;;;;;;;;;;
0041;;Hd;;;;;;;;;;;;
0042;;Hd;;;;;;;;;;;;
0043;;Hd;;;;;;;;;;;;
0044;;Hd;;;;;;;;;;;;
0045;;Hd;;;;;;;;;;;;
0046;;Hd;;;;;;;;;;;;
0061;;Hd;;;;;;;;;;;;
0062;;Hd;;;;;;;;;;;;
0063;;Hd;;;;;;;;;;;;
0064;;Hd;;;;;;;;;;;;
0065;;Hd;;;;;;;;;;;;
0066;;Hd;;;;;;;;;;;;
FF10;;Hd;;;;;;;;;;;;
FF11;;Hd;;;;;;;;;;;;
FF12;;Hd;;;;;;;;;;;;
FF13;;Hd;;;;;;;;;;;;
FF14;;Hd;;;;;;;;;;;;
FF15;;Hd;;;;;;;;;;;;
FF16;;Hd;;;;;;;;;;;;
FF17;;Hd;;;;;;;;;;;;
FF18;;Hd;;;;;;;;;;;;
FF19;;Hd;;;;;;;;;;;;
FF21;;Hd;;;;;;;;;;;;
FF22;;Hd;;;;;;;;;;;;
FF23;;Hd;;;;;;;;;;;;
FF24;;Hd;;;;;;;;;;;;
FF25;;Hd;;;;;;;;;;;;
FF26;;Hd;;;;;;;;;;;;
FF41;;Hd;;;;;;;;;;;;
FF42;;Hd;;;;;;;;;;;;
FF43;;Hd;;;;;;;;;;;;
FF44;;Hd;;;;;;;;;;;;
FF45;;Hd;;;;;;;;;;;;
FF46;;Hd;;;;;;;;;;;;
#
# Quote marks.
#
0022;;Qm;;;;;;;;;;;;
0027;;Qm;;;;;;;;;;;;
00AB;;Qm;;;;;;;;;;;;
00BB;;Qm;;;;;;;;;;;;
2018;;Qm;;;;;;;;;;;;
2019;;Qm;;;;;;;;;;;;
201A;;Qm;;;;;;;;;;;;
201B;;Qm;;;;;;;;;;;;
201C;;Qm;;;;;;;;;;;;
201D;;Qm;;;;;;;;;;;;
201E;;Qm;;;;;;;;;;;;
201F;;Qm;;;;;;;;;;;;
2039;;Qm;;;;;;;;;;;;
203A;;Qm;;;;;;;;;;;;
300C;;Qm;;;;;;;;;;;;
300D;;Qm;;;;;;;;;;;;
300E;;Qm;;;;;;;;;;;;
300F;;Qm;;;;;;;;;;;;
301D;;Qm;;;;;;;;;;;;
301E;;Qm;;;;;;;;;;;;
301F;;Qm;;;;;;;;;;;;
FE41;;Qm;;;;;;;;;;;;
FE42;;Qm;;;;;;;;;;;;
FE43;;Qm;;;;;;;;;;;;
FE44;;Qm;;;;;;;;;;;;
FF02;;Qm;;;;;;;;;;;;
FF07;;Qm;;;;;;;;;;;;
FF62;;Qm;;;;;;;;;;;;
FF63;;Qm;;;;;;;;;;;;

View File

@ -1,207 +0,0 @@
#
# $Id: UCDATAREADME.txt,v 1.1 1999/01/08 00:19:20 ftang%netscape.com Exp $
#
MUTT UCData Package 1.9
-----------------------
This is a package that supports ctype-like operations for Unicode UCS-2 text
(and surrogates), case mapping, and decomposition lookup. To use it, you will
need to get the "UnicodeData-2.0.14.txt" (or later) file from the Unicode Web
or FTP site.
This package consists of two parts:
1. A program called "ucgendat" which generates five data files from the
UnicodeData-2.*.txt file. The files are:
A. case.dat - the case mappings.
B. ctype.dat - the character property tables.
C. decomp.dat - the character decompositions.
D. cmbcl.dat - the non-zero combining classes.
E. num.dat - the codes representing numbers.
2. The "ucdata.[ch]" files which implement the functions needed to
check to see if a character matches groups of properties, to map between
upper, lower, and title case, to look up the decomposition of a
character, look up the combining class of a character, and get the number
value of a character.
A short reference to the functions available is in the "api.txt" file.
Techie Details
==============
The "ucgendat" program parses files from the command line which are all in the
Unicode Character Database (UCDB) format. An additional properties file,
"MUTTUCData.txt", provides some extra properties for some characters.
The program looks for the two character properties fields (2 and 4), the
combining class field (3), the decomposition field (5), the numeric value
field (8), and the case mapping fields (12, 13, and 14). The decompositions
are recursively expanded before being written out.
The decomposition table contains all the canonical decompositions. This means
all decompositions that do not have tags such as "<compat>" or "<font>".
The data is almost all stored as unsigned longs (32-bits assumed) and the
routines that load the data take care of endian swaps when necessary. This
also means that surrogates (>= 0x10000) can be placed in the data files the
"ucgendat" program parses.
The data is written as external files and broken into five parts so it can be
selectively updated at runtime if necessary.
The data files currently generated from the "ucgendat" program total about 56K
in size all together.
The format of the binary data files is documented in the "format.txt" file.
Mark Leisher <mleisher@crl.nmsu.edu>
13 December 1998
CHANGES
=======
Version 1.9
-----------
1. Fixed a problem with an incorrect amount of storage being allocated for the
combining class nodes.
2. Fixed an invalid initialization in the number code.
3. Changed the Java template file formatting a bit.
4. Added tables and function for getting decompositions in the Java class.
Version 1.8
-----------
1. Fixed a problem with adding certain ranges.
2. Added two more macros for testing for identifiers.
3. Tested with the UnicodeData-2.1.5.txt file.
Version 1.7
-----------
1. Fixed a problem with looking up decompositions in "ucgendat."
Version 1.6
-----------
1. Added two new properties introduced with UnicodeData-2.1.4.txt.
2. Changed the "ucgendat.c" program a little to automatically align the
property data on a 4-byte boundary when new properties are added.
3. Changed the "ucgendat.c" programs to only generate canonical
decompositions.
4. Added two new macros ucisinitialpunct() and ucisfinalpunct() to check for
initial and final punctuation characters.
5. Minor additions and changes to the documentation.
Version 1.5
-----------
1. Changed all file open calls to include binary mode with "b" for DOS/WIN
platforms.
2. Wrapped the unistd.h include so it won't be included when compiled under
Win32.
3. Fixed a bad range check for hex digits in ucgendat.c.
4. Fixed a bad endian swap for combining classes.
5. Added code to make a number table and associated lookup functions.
Functions added are ucnumber(), ucdigit(), and ucgetnumber(). The last
function is to maintain compatibility with John Cowan's "uctype" package.
Version 1.4
-----------
1. Fixed a bug with adding a range.
2. Fixed a bug with inserting a range in order.
3. Fixed incorrectly specified ucisdefined() and ucisundefined() macros.
4. Added the missing unload for the combining class data.
5. Fixed a bad macro placement in ucisweak().
Version 1.3
-----------
1. Bug with case mapping calculations fixed.
2. Bug with empty character property entries fixed.
3. Bug with incorrect type in the combining class lookup fixed.
4. Some corrections done to api.txt.
5. Bug in certain character property lookups fixed.
6. Added a character property table that records the defined characters.
7. Replaced ucisunknown() with ucisdefined() and ucisundefined().
Version 1.2
-----------
1. Added code to ucgendat to generate a combining class table.
2. Fixed an endian problem with the byte count of decompositions.
3. Fixed some minor problems in the "format.txt" file.
4. Removed some bogus "Ss" values from MUTTUCData.txt file.
5. Added API function to get combining class.
6. Changed the open mode to "rb" so binary data files will be opened correctly
on DOS/WIN as well as other platforms.
7. Added the "api.txt" file.
Version 1.1
-----------
1. Added ucisxdigit() which I overlooked.
2. Added UC_LT to the ucisalpha() macro which I overlooked.
3. Change uciscntrl() to include UC_CF.
4. Added ucisocntrl() and ucfntcntrl() macros.
5. Added a ucisblank() which I overlooked.
6. Added missing properties to ucissymbol() and ucisnumber().
7. Added ucisgraph() and ucisprint().
8. Changed the "Mr" property to "Sy" to mark this subset of mirroring
characters as symmetric to avoid trampling the Unicode/ISO10646 sense of
mirroring.
9. Added another property called "Ss" which includes control characters
traditionally seen as spaces in the isspace() macro.
10. Added a bunch of macros to be API compatible with John Cowan's package.
ACKNOWLEDGEMENTS
================
Thanks go to John Cowan <cowan@locke.ccil.org> for pointing out lots of
missing things and giving me stuff, particularly a bunch of new macros.
Thanks go to Bob Verbrugge <bob_verbrugge@nl.compuware.com> for pointing out
various bugs.
Thanks go to Christophe Pierret <cpierret@businessobjects.com> for pointing
out that file modes need to have "b" for DOS/WIN machines, pointing out
unistd.h is not a Win 32 header, and pointing out a problem with ucisalnum().
Thanks go to Kent Johnson <kent@pondview.mv.com> for finding a bug that caused
incomplete decompositions to be generated by the "ucgendat" program.
Thanks go to Valeriy E. Ushakov <uwe@ptc.spbu.ru> for spotting an allocation
error and an initialization error.

Binary file not shown.

Binary file not shown.

View File

@ -1,243 +0,0 @@
#
# $Id: format.txt,v 1.1 1999/01/08 00:19:20 ftang%netscape.com Exp $
#
CHARACTER DATA
==============
This package generates some data files that contain character properties useful
for text processing.
CHARACTER PROPERTIES
====================
The first data file is called "ctype.dat" and contains a compressed form of
the character properties found in the Unicode Character Database (UCDB).
Additional properties can be specified in limited UCDB format in another file
to avoid modifying the original UCDB.
The following is a property name and code table to be used with the character
data:
NAME CODE DESCRIPTION
---------------------
Mn 0 Mark, Non-Spacing
Mc 1 Mark, Spacing Combining
Me 2 Mark, Enclosing
Nd 3 Number, Decimal Digit
Nl 4 Number, Letter
No 5 Number, Other
Zs 6 Separator, Space
Zl 7 Separator, Line
Zp 8 Separator, Paragraph
Cc 9 Other, Control
Cf 10 Other, Format
Cs 11 Other, Surrogate
Co 12 Other, Private Use
Cn 13 Other, Not Assigned
Lu 14 Letter, Uppercase
Ll 15 Letter, Lowercase
Lt 16 Letter, Titlecase
Lm 17 Letter, Modifier
Lo 18 Letter, Other
Pc 19 Punctuation, Connector
Pd 20 Punctuation, Dash
Ps 21 Punctuation, Open
Pe 22 Punctuation, Close
Po 23 Punctuation, Other
Sm 24 Symbol, Math
Sc 25 Symbol, Currency
Sk 26 Symbol, Modifier
So 27 Symbol, Other
L 28 Left-To-Right
R 29 Right-To-Left
EN 30 European Number
ES 31 European Number Separator
ET 32 European Number Terminator
AN 33 Arabic Number
CS 34 Common Number Separator
B 35 Block Separator
S 36 Segment Separator
WS 37 Whitespace
ON 38 Other Neutrals
Pi 47 Punctuation, Initial
Pf 48 Punctuation, Final
#
# Implementation specific properties.
#
Cm 39 Composite
Nb 40 Non-Breaking
Sy 41 Symmetric (characters which are part of open/close pairs)
Hd 42 Hex Digit
Qm 43 Quote Mark
Mr 44 Mirroring
Ss 45 Space, Other (controls viewed as spaces in ctype isspace())
Cp 46 Defined character
The actual binary data is formatted as follows:
Assumptions: unsigned short is at least 16-bits in size and unsigned long
is at least 32-bits in size.
unsigned short ByteOrderMark
unsigned short OffsetArraySize
unsigned long Bytes
unsigned short Offsets[OffsetArraySize + 1]
unsigned long Ranges[N], N = value of Offsets[OffsetArraySize]
The Bytes field provides the total byte count used for the Offsets[] and
Ranges[] arrays. The Offsets[] array is aligned on a 4-byte boundary and
there is always one extra node on the end to hold the final index of the
Ranges[] array. The Ranges[] array contains pairs of 4-byte values
representing a range of Unicode characters. The pairs are arranged in
increasing order by the first character code in the range.
Determining if a particular character is in the property list requires a
simple binary search to determine if a character is in any of the ranges
for the property.
If the ByteOrderMark is equal to 0xFFFE, then the data was generated on a
machine with a different endian order and the values must be byte-swapped.
To swap a 16-bit value:
c = (c >> 8) | ((c & 0xff) << 8)
To swap a 32-bit value:
c = ((c & 0xff) << 24) | (((c >> 8) & 0xff) << 16) |
(((c >> 16) & 0xff) << 8) | (c >> 24)
CASE MAPPINGS
=============
The next data file is called "case.dat" and contains three case mapping tables
in the following order: upper, lower, and title case. Each table is in
increasing order by character code and each mapping contains 3 unsigned longs
which represent the possible mappings.
The format for the binary form of these tables is:
unsigned short ByteOrderMark
unsigned short NumMappingNodes, count of all mapping nodes
unsigned short CaseTableSizes[2], upper and lower mapping node counts
unsigned long CaseTables[NumMappingNodes]
The starting indexes of the case tables are calculated as following:
UpperIndex = 0;
LowerIndex = CaseTableSizes[0] * 3;
TitleIndex = LowerIndex + CaseTableSizes[1] * 3;
The order of the fields for the three tables are:
Upper case
----------
unsigned long upper;
unsigned long lower;
unsigned long title;
Lower case
----------
unsigned long lower;
unsigned long upper;
unsigned long title;
Title case
----------
unsigned long title;
unsigned long upper;
unsigned long lower;
If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
same way as described in the CHARACTER PROPERTIES section.
Because the tables are in increasing order by character code, locating a
mapping requires a simple binary search on one of the 3 codes that make up
each node.
It is important to note that there can only be 65536 mapping nodes which
divided into 3 portions allows 21845 nodes for each case mapping table. The
distribution of mappings may be more or less than 21845 per table, but only
65536 are allowed.
DECOMPOSITIONS
==============
The next data file is called "decomp.dat" and contains the decomposition data
for all characters with decompositions containing more than one character and
are *not* compatibility decompositions. Compatibility decompositions are
signaled in the UCDB format by the use of the <compat> tag in the
decomposition field. Each list of character codes represents a full
decomposition of a composite character. The nodes are arranged in increasing
order by character code.
The format for the binary form of this table is:
unsigned short ByteOrderMark
unsigned short NumDecompNodes, count of all decomposition nodes
unsigned long Bytes
unsigned long DecompNodes[(NumDecompNodes * 2) + 1]
unsigned long Decomp[N], N = sum of all counts in DecompNodes[]
If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
same way as described in the CHARACTER PROPERTIES section.
The DecompNodes[] array consists of pairs of unsigned longs, the first of
which is the character code and the second is the initial index of the list
of character codes representing the decomposition.
Locating the decomposition of a composite character requires a binary search
for a character code in the DecompNodes[] array and using its index to
locate the start of the decomposition. The length of the decomposition list
is the index in the following element in DecompNode[] minus the current
index.
COMBINING CLASSES
=================
The fourth data file is called "cmbcl.dat" and contains the characters with
non-zero combining classes.
The format for the binary form of this table is:
unsigned short ByteOrderMark
unsigned short NumCCLNodes
unsigned long Bytes
unsigned long CCLNodes[NumCCLNodes * 3]
If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
same way as described in the CHARACTER PROPERTIES section.
The CCLNodes[] array consists of groups of three unsigned longs. The first
and second are the beginning and ending of a range and the third is the
combining class of that range.
If a character is not found in this table, then the combining class is
assumed to be 0.
It is important to note that only 65536 distinct ranges plus combining class
can be specified because the NumCCLNodes is usually a 16-bit number.
NUMBER TABLE
============
The final data file is called "num.dat" and contains the characters that have
a numeric value associated with them.
The format for the binary form of the table is:
unsigned short ByteOrderMark
unsigned short NumNumberNodes
unsigned long Bytes
unsigned long NumberNodes[NumNumberNodes]
unsigned short ValueNodes[(Bytes - (NumNumberNodes * sizeof(unsigned long)))
/ sizeof(short)]
If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
same way as described in the CHARACTER PROPERTIES section.
The NumberNodes array contains pairs of values, the first of which is the
character code and the second an index into the ValueNodes array. The
ValueNodes array contains pairs of integers which represent the numerator
and denominator of the numeric value of the character. If the character
happens to map to an integer, both the values in ValueNodes will be the
same.

View File

@ -1,12 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Program('ucgendat')
SOURCES += [
'ucgendat.c',
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,306 +0,0 @@
/*
* Copyright 1996, 1997, 1998 Computing Research Labs,
* New Mexico State University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _h_ucdata
#define _h_ucdata
/*
* $Id: ucdata.h,v 1.1 1999/01/08 00:19:12 ftang%netscape.com Exp $
*/
#ifdef __cplusplus
extern "C" {
#endif
#undef __
#ifdef __STDC__
#define __(x) x
#else
#define __(x) ()
#endif
#define UCDATA_VERSION "1.9"
/**************************************************************************
*
* Masks and macros for character properties.
*
**************************************************************************/
/*
* Values that can appear in the `mask1' parameter of the ucisprop()
* function.
*/
#define UC_MN 0x00000001 /* Mark, Non-Spacing */
#define UC_MC 0x00000002 /* Mark, Spacing Combining */
#define UC_ME 0x00000004 /* Mark, Enclosing */
#define UC_ND 0x00000008 /* Number, Decimal Digit */
#define UC_NL 0x00000010 /* Number, Letter */
#define UC_NO 0x00000020 /* Number, Other */
#define UC_ZS 0x00000040 /* Separator, Space */
#define UC_ZL 0x00000080 /* Separator, Line */
#define UC_ZP 0x00000100 /* Separator, Paragraph */
#define UC_CC 0x00000200 /* Other, Control */
#define UC_CF 0x00000400 /* Other, Format */
#define UC_OS 0x00000800 /* Other, Surrogate */
#define UC_CO 0x00001000 /* Other, Private Use */
#define UC_CN 0x00002000 /* Other, Not Assigned */
#define UC_LU 0x00004000 /* Letter, Uppercase */
#define UC_LL 0x00008000 /* Letter, Lowercase */
#define UC_LT 0x00010000 /* Letter, Titlecase */
#define UC_LM 0x00020000 /* Letter, Modifier */
#define UC_LO 0x00040000 /* Letter, Other */
#define UC_PC 0x00080000 /* Punctuation, Connector */
#define UC_PD 0x00100000 /* Punctuation, Dash */
#define UC_PS 0x00200000 /* Punctuation, Open */
#define UC_PE 0x00400000 /* Punctuation, Close */
#define UC_PO 0x00800000 /* Punctuation, Other */
#define UC_SM 0x01000000 /* Symbol, Math */
#define UC_SC 0x02000000 /* Symbol, Currency */
#define UC_SK 0x04000000 /* Symbol, Modifier */
#define UC_SO 0x08000000 /* Symbol, Other */
#define UC_L 0x10000000 /* Left-To-Right */
#define UC_R 0x20000000 /* Right-To-Left */
#define UC_EN 0x40000000 /* European Number */
#define UC_ES 0x80000000 /* European Number Separator */
/*
* Values that can appear in the `mask2' parameter of the ucisprop()
* function.
*/
#define UC_ET 0x00000001 /* European Number Terminator */
#define UC_AN 0x00000002 /* Arabic Number */
#define UC_CS 0x00000004 /* Common Number Separator */
#define UC_B 0x00000008 /* Block Separator */
#define UC_S 0x00000010 /* Segment Separator */
#define UC_WS 0x00000020 /* Whitespace */
#define UC_ON 0x00000040 /* Other Neutrals */
/*
* Implementation specific character properties.
*/
#define UC_CM 0x00000080 /* Composite */
#define UC_NB 0x00000100 /* Non-Breaking */
#define UC_SY 0x00000200 /* Symmetric */
#define UC_HD 0x00000400 /* Hex Digit */
#define UC_QM 0x00000800 /* Quote Mark */
#define UC_MR 0x00001000 /* Mirroring */
#define UC_SS 0x00002000 /* Space, other */
#define UC_CP 0x00004000 /* Defined */
/*
* Added for UnicodeData-2.1.3.
*/
#define UC_PI 0x00008000 /* Punctuation, Initial */
#define UC_PF 0x00010000 /* Punctuation, Final */
/*
* This is the primary function for testing to see if a character has some set
* of properties. The macros that test for various character properties all
* call this function with some set of masks.
*/
extern int ucisprop __((unsigned long code, unsigned long mask1,
unsigned long mask2));
#define ucisalpha(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT, 0)
#define ucisdigit(cc) ucisprop(cc, UC_ND, 0)
#define ucisalnum(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT|UC_ND, 0)
#define uciscntrl(cc) ucisprop(cc, UC_CC|UC_CF, 0)
#define ucisspace(cc) ucisprop(cc, UC_ZS|UC_SS, 0)
#define ucisblank(cc) ucisprop(cc, UC_ZS, 0)
#define ucispunct(cc) ucisprop(cc, UC_PD|UC_PS|UC_PE|UC_PO, UC_PI|UC_PF)
#define ucisgraph(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\
UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\
UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\
UC_SO, UC_PI|UC_PF)
#define ucisprint(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\
UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\
UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\
UC_SO|UC_ZS, UC_PI|UC_PF)
#define ucisupper(cc) ucisprop(cc, UC_LU, 0)
#define ucislower(cc) ucisprop(cc, UC_LL, 0)
#define ucistitle(cc) ucisprop(cc, UC_LT, 0)
#define ucisxdigit(cc) ucisprop(cc, 0, UC_HD)
#define ucisisocntrl(cc) ucisprop(cc, UC_CC, 0)
#define ucisfmtcntrl(cc) ucisprop(cc, UC_CF, 0)
#define ucissymbol(cc) ucisprop(cc, UC_SM|UC_SC|UC_SO|UC_SK, 0)
#define ucisnumber(cc) ucisprop(cc, UC_ND|UC_NO|UC_NL, 0)
#define ucisnonspacing(cc) ucisprop(cc, UC_MN, 0)
#define ucisopenpunct(cc) ucisprop(cc, UC_PS, 0)
#define ucisclosepunct(cc) ucisprop(cc, UC_PE, 0)
#define ucisinitialpunct(cc) ucisprop(cc, 0, UC_PI)
#define ucisfinalpunct(cc) ucisprop(cc, 0, UC_PF)
#define uciscomposite(cc) ucisprop(cc, 0, UC_CM)
#define ucishex(cc) ucisprop(cc, 0, UC_HD)
#define ucisquote(cc) ucisprop(cc, 0, UC_QM)
#define ucissymmetric(cc) ucisprop(cc, 0, UC_SY)
#define ucismirroring(cc) ucisprop(cc, 0, UC_MR)
#define ucisnonbreaking(cc) ucisprop(cc, 0, UC_NB)
/*
* Directionality macros.
*/
#define ucisrtl(cc) ucisprop(cc, UC_R, 0)
#define ucisltr(cc) ucisprop(cc, UC_L, 0)
#define ucisstrong(cc) ucisprop(cc, UC_L|UC_R, 0)
#define ucisweak(cc) ucisprop(cc, UC_EN|UC_ES, UC_ET|UC_AN|UC_CS)
#define ucisneutral(cc) ucisprop(cc, 0, UC_B|UC_S|UC_WS|UC_ON)
#define ucisseparator(cc) ucisprop(cc, 0, UC_B|UC_S)
/*
* Other macros inspired by John Cowan.
*/
#define ucismark(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME, 0)
#define ucismodif(cc) ucisprop(cc, UC_LM, 0)
#define ucisletnum(cc) ucisprop(cc, UC_NL, 0)
#define ucisconnect(cc) ucisprop(cc, UC_PC, 0)
#define ucisdash(cc) ucisprop(cc, UC_PD, 0)
#define ucismath(cc) ucisprop(cc, UC_SM, 0)
#define uciscurrency(cc) ucisprop(cc, UC_SC, 0)
#define ucismodifsymbol(cc) ucisprop(cc, UC_SK, 0)
#define ucisnsmark(cc) ucisprop(cc, UC_MN, 0)
#define ucisspmark(cc) ucisprop(cc, UC_MC, 0)
#define ucisenclosing(cc) ucisprop(cc, UC_ME, 0)
#define ucisprivate(cc) ucisprop(cc, UC_CO, 0)
#define ucissurrogate(cc) ucisprop(cc, UC_OS, 0)
#define ucislsep(cc) ucisprop(cc, UC_ZL, 0)
#define ucispsep(cc) ucisprop(cc, UC_ZP, 0)
#define ucisidentstart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL, 0)
#define ucisidentpart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL|\
UC_MN|UC_MC|UC_ND|UC_PC|UC_CF, 0)
#define ucisdefined(cc) ucisprop(cc, 0, UC_CP)
#define ucisundefined(cc) !ucisprop(cc, 0, UC_CP)
/*
* Other miscellaneous character property macros.
*/
#define ucishan(cc) (((cc) >= 0x4e00 && (cc) <= 0x9fff) ||\
((cc) >= 0xf900 && (cc) <= 0xfaff))
#define ucishangul(cc) ((cc) >= 0xac00 && (cc) <= 0xd7ff)
/**************************************************************************
*
* Functions for case conversion.
*
**************************************************************************/
extern unsigned long uctoupper __((unsigned long code));
extern unsigned long uctolower __((unsigned long code));
extern unsigned long uctotitle __((unsigned long code));
/**************************************************************************
*
* Functions for getting decompositions.
*
**************************************************************************/
/*
* This routine determines if the code has a decomposition. If it returns 0,
* there is no decomposition. Any other value indicates a decomposition was
* returned.
*/
extern int ucdecomp __((unsigned long code, unsigned long *num,
unsigned long **decomp));
/*
* If the code is a Hangul syllable, this routine decomposes it into the array
* passed. The array size should be at least 3.
*/
extern int ucdecomp_hangul __((unsigned long code, unsigned long *num,
unsigned long decomp[]));
/**************************************************************************
*
* Functions for getting combining classes.
*
**************************************************************************/
/*
* This will return the combining class for a character to be used with the
* Canonical Ordering algorithm.
*/
extern unsigned long uccombining_class __((unsigned long code));
/**************************************************************************
*
* Functions for getting numbers and digits.
*
**************************************************************************/
struct ucnumber {
int numerator;
int denominator;
};
extern int ucnumber_lookup __((unsigned long code, struct ucnumber *num));
extern int ucdigit_lookup __((unsigned long code, int *digit));
/*
* For compatibility with John Cowan's "uctype" package.
*/
extern struct ucnumber ucgetnumber __((unsigned long code));
extern int ucgetdigit __((unsigned long code));
/**************************************************************************
*
* Functions library initialization and cleanup.
*
**************************************************************************/
/*
* Macros for specifying the data tables to be loaded for ucdata_load().
*/
#define UCDATA_CASE 0x01
#define UCDATA_CTYPE 0x02
#define UCDATA_DECOMP 0x04
#define UCDATA_CMBCL 0x08
#define UCDATA_NUM 0x10
#define UCDATA_ALL (UCDATA_CASE|UCDATA_CTYPE|UCDATA_DECOMP|\
UCDATA_CMBCL|UCDATA_NUM)
/*
* Functions to load, unload, and reload specific data files.
*/
extern void ucdata_load __((char *paths, int mask));
extern void ucdata_unload __((int mask));
extern void ucdata_reload __((char *paths, int mask));
/*
* Deprecated functions, now just compatibility macros.
*/
#define ucdata_setup(p) ucdata_load(p, UCDATA_ALL)
#define ucdata_cleanup() ucdata_unload(UCDATA_ALL)
#undef __
#ifdef __cplusplus
}
#endif
#endif /* _h_ucdata */

View File

@ -33,15 +33,8 @@ RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(JS::shadow::Zone* shadow
bool
IsMarkedBlack(JSObject* obj)
{
// Note: we assume conservatively that Nursery things will be live.
if (!obj->isTenured())
return true;
gc::TenuredCell& tenured = obj->asTenured();
if (tenured.isMarkedAny() || tenured.arena()->allocatedDuringIncremental)
return true;
return false;
return obj->isMarkedBlack() ||
(obj->isTenured() && obj->asTenured().arena()->allocatedDuringIncremental);
}
bool

View File

@ -5,6 +5,7 @@ if (!wasmDebuggingIsSupported())
quit();
var g = newGlobal();
var dbg = new Debugger(g);
g.eval(`
function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); }
o1 = initWasm('(module (func) (export "" 0))');
@ -19,7 +20,6 @@ function isValidWasmURL(url) {
return /^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(url);
}
var dbg = new Debugger(g);
var foundScripts = dbg.findScripts().filter(isWasm);
assertEq(foundScripts.length, 2);
assertEq(isValidWasmURL(foundScripts[0].source.url), true);

View File

@ -332,7 +332,6 @@ CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph, MacroAssembler*
CodeGenerator::~CodeGenerator()
{
MOZ_ASSERT_IF(!gen->compilingWasm(), masm.numSymbolicAccesses() == 0);
js_delete(scriptCounts_);
}

View File

@ -15,7 +15,6 @@ template <AllowGC allowGC>
JitCode*
Linker::newCode(JSContext* cx, CodeKind kind, bool hasPatchableBackedges /* = false */)
{
MOZ_ASSERT(masm.numSymbolicAccesses() == 0);
MOZ_ASSERT_IF(hasPatchableBackedges, kind == ION_CODE);
gc::AutoSuppressGC suppressGC(cx);

View File

@ -90,10 +90,10 @@ MacroAssembler::call(const wasm::CallSiteDesc& desc, const Register reg)
}
void
MacroAssembler::call(const wasm::CallSiteDesc& desc, uint32_t funcDefIndex)
MacroAssembler::call(const wasm::CallSiteDesc& desc, uint32_t funcIndex)
{
CodeOffset l = callWithPatch();
append(desc, l, funcDefIndex);
append(desc, l, funcIndex);
}
void

View File

@ -3136,7 +3136,7 @@ MacroAssembler::wasmEmitTrapOutOfLineCode()
// iterator to find the right CodeRange while walking the stack.
breakpoint();
clearTrapSites();
trapSites().clear();
}
void

View File

@ -619,6 +619,13 @@ js::jit::ReleaseProcessExecutableMemory()
execMemory.release();
}
size_t
js::jit::LikelyAvailableExecutableMemory()
{
// Round down available memory to the closest MB.
return MaxCodeBytesPerProcess - AlignBytes(execMemory.bytesAllocated(), 0x100000U);
}
bool
js::jit::CanLikelyAllocateMoreExecutableMemory()
{

View File

@ -50,6 +50,11 @@ extern void DeallocateExecutableMemory(void* addr, size_t bytes);
// function.
extern bool CanLikelyAllocateMoreExecutableMemory();
// Returns a rough guess of how much executable memory remains available,
// rounded down to MB limit. Note this can fluctuate as other threads within
// the process allocate executable memory.
extern size_t LikelyAvailableExecutableMemory();
} // namespace jit
} // namespace js

View File

@ -659,15 +659,31 @@ Assembler::finish()
}
bool
Assembler::asmMergeWith(Assembler& other)
Assembler::appendRawCode(const uint8_t* code, size_t numBytes)
{
flush();
other.flush();
if (other.oom())
return m_buffer.appendRawCode(code, numBytes);
}
bool
Assembler::reserve(size_t size)
{
// This buffer uses fixed-size chunks so there's no point in reserving
// now vs. on-demand.
return !oom();
}
bool
Assembler::swapBuffer(wasm::Bytes& bytes)
{
// For now, specialize to the one use case. As long as wasm::Bytes is a
// Vector, not a linked-list of chunks, there's not much we can do other
// than copy.
MOZ_ASSERT(bytes.empty());
if (!bytes.resize(bytesNeeded()))
return false;
if (!AssemblerShared::asmMergeWith(size(), other))
return false;
return m_buffer.appendBuffer(other.m_buffer);
m_buffer.executableCopy(bytes.begin());
return true;
}
void
@ -948,7 +964,7 @@ Assembler::processCodeLabels(uint8_t* rawCode)
{
for (size_t i = 0; i < codeLabels_.length(); i++) {
CodeLabel label = codeLabels_[i];
Bind(rawCode, label.patchAt(), rawCode + label.target()->offset());
Bind(rawCode, *label.patchAt(), *label.target());
}
}
@ -960,9 +976,9 @@ Assembler::writeCodePointer(CodeOffset* label)
}
void
Assembler::Bind(uint8_t* rawCode, CodeOffset* label, const void* address)
Assembler::Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target)
{
*reinterpret_cast<const void**>(rawCode + label->offset()) = address;
*reinterpret_cast<const void**>(rawCode + label.offset()) = rawCode + target.offset();
}
Assembler::Condition

View File

@ -1402,7 +1402,9 @@ class Assembler : public AssemblerShared
bool isFinished;
public:
void finish();
bool asmMergeWith(Assembler& other);
bool appendRawCode(const uint8_t* code, size_t numBytes);
bool reserve(size_t size);
bool swapBuffer(wasm::Bytes& bytes);
void copyJumpRelocationTable(uint8_t* dest);
void copyDataRelocationTable(uint8_t* dest);
@ -1712,12 +1714,7 @@ class Assembler : public AssemblerShared
// I'm going to pretend this doesn't exist for now.
void retarget(Label* label, void* target, Relocation::Kind reloc);
void Bind(uint8_t* rawCode, CodeOffset* label, const void* address);
// See Bind
size_t labelToPatchOffset(CodeOffset label) {
return label.offset();
}
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target);
void as_bkpt();
@ -1919,10 +1916,6 @@ class Assembler : public AssemblerShared
ImmPtr expectedValue);
static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm);
static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm) {
MOZ_CRASH("Unused.");
}
static uint32_t AlignDoubleArg(uint32_t offset) {
return (offset + 1) & ~1;
}

View File

@ -1565,21 +1565,19 @@ Simulator::startInterrupt(WasmActivation* activation)
void
Simulator::handleWasmInterrupt()
{
void* pc = (void*)get_pc();
uint8_t* pc = (uint8_t*)get_pc();
uint8_t* fp = (uint8_t*)get_register(r11);
WasmActivation* activation = wasm::ActivationIfInnermost(cx_);
const wasm::CodeSegment* segment;
const wasm::Code* code = activation->compartment()->wasm.lookupCode(pc, &segment);
if (!code || !segment->containsFunctionPC(pc))
const wasm::CodeSegment* cs = nullptr;
if (!wasm::InInterruptibleCode(cx_, pc, &cs))
return;
// fp can be null during the prologue/epilogue of the entry function.
if (!fp)
return;
startInterrupt(activation);
set_pc(int32_t(segment->interruptCode()));
startInterrupt(wasm::ActivationIfInnermost(cx_));
set_pc(int32_t(cs->interruptCode()));
}
// WebAssembly memories contain an extra region of guard pages (see

View File

@ -631,12 +631,6 @@ Assembler::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader
cx->zone()->group()->storeBuffer().putWholeCell(code);
}
void
Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm)
{
MOZ_CRASH("PatchInstructionImmediate()");
}
void
Assembler::retarget(Label* label, Label* target)
{

View File

@ -189,7 +189,13 @@ class Assembler : public vixl::Assembler
typedef vixl::Condition Condition;
void finish();
bool asmMergeWith(const Assembler& other) {
bool appendRawCode(const uint8_t* code, size_t numBytes) {
MOZ_CRASH("NYI");
}
bool reserve(size_t size) {
MOZ_CRASH("NYI");
}
bool swapBuffer(wasm::Bytes& bytes) {
MOZ_CRASH("NYI");
}
void trace(JSTracer* trc);
@ -245,12 +251,12 @@ class Assembler : public vixl::Assembler
void processCodeLabels(uint8_t* rawCode) {
for (size_t i = 0; i < codeLabels_.length(); i++) {
CodeLabel label = codeLabels_[i];
Bind(rawCode, label.patchAt(), rawCode + label.target()->offset());
Bind(rawCode, *label.patchAt(), *label.target());
}
}
void Bind(uint8_t* rawCode, CodeOffset* label, const void* address) {
*reinterpret_cast<const void**>(rawCode + label->offset()) = address;
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset address) {
*reinterpret_cast<const void**>(rawCode + label.offset()) = rawCode + address.offset();
}
void retarget(Label* cur, Label* next);
@ -270,9 +276,6 @@ class Assembler : public vixl::Assembler
ARMBuffer::PoolEntry pe(curOffset);
return armbuffer_.poolEntryOffset(pe);
}
size_t labelToPatchOffset(CodeOffset label) {
return label.offset();
}
static uint8_t* PatchableJumpAddress(JitCode* code, uint32_t index) {
return code->raw() + index;
}
@ -344,8 +347,6 @@ class Assembler : public vixl::Assembler
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
const ObjectVector& nurseryObjects);

View File

@ -32,7 +32,8 @@
#include "js/Utility.h"
#include "threading/LockGuard.h"
#include "vm/Runtime.h"
#include "wasm/WasmCode.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmSignalHandlers.h"
js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr;
@ -240,23 +241,25 @@ void Simulator::trigger_wasm_interrupt() {
// the current PC may have advanced once since the signal handler's guard. So we
// re-check here.
void Simulator::handle_wasm_interrupt() {
void* pc = (void*)get_pc();
uint8_t* pc = (uint8_t*)get_pc();
uint8_t* fp = (uint8_t*)xreg(30);
js::WasmActivation* activation = js::wasm::ActivationIfInnermost(cx_);
const js::wasm::CodeSegment* segment;
const js::wasm::Code* code = activation->compartment()->wasm.lookupCode(pc, &segment);
if (!code || !segment->containsFunctionPC(pc))
return;
const js::wasm::CodeSegment* cs = nullptr;
if (!js::wasm::InInterruptibleCode(cx_, pc, &cs))
return;
// fp can be null during the prologue/epilogue of the entry function.
if (!fp)
return;
JS::ProfilingFrameIterator::RegisterState state;
state.pc = pc;
state.fp = fp;
state.lr = (uint8_t*) xreg(30);
state.sp = (uint8_t*) xreg(31);
activation->startInterrupt(state);
js::wasm::ActivationIfInnermost(cx_)->startInterrupt(state);
set_pc((Instruction*)segment->interruptCode());
set_pc((Instruction*)cs->interruptCode());
}

View File

@ -96,15 +96,9 @@ AssemblerMIPSShared::finish()
}
bool
AssemblerMIPSShared::asmMergeWith(const AssemblerMIPSShared& other)
AssemblerMIPSShared::appendRawCode(const uint8_t* code, size_t numBytes)
{
if (!AssemblerShared::asmMergeWith(size(), other))
return false;
for (size_t i = 0; i < other.numLongJumps(); i++) {
size_t off = other.longJumps_[i];
addLongJump(BufferOffset(size() + off));
}
return m_buffer.appendBuffer(other.m_buffer);
return m_buffer.appendRawCode(code, numBytes);
}
uint32_t
@ -138,7 +132,7 @@ AssemblerMIPSShared::processCodeLabels(uint8_t* rawCode)
{
for (size_t i = 0; i < codeLabels_.length(); i++) {
CodeLabel label = codeLabels_[i];
Bind(rawCode, label.patchAt(), rawCode + label.target()->offset());
Bind(rawCode, *label.patchAt(), *label.target());
}
}

View File

@ -748,16 +748,11 @@ class MIPSBufferWithExecutableCopy : public MIPSBuffer
}
}
bool appendBuffer(const MIPSBufferWithExecutableCopy& other) {
bool appendRawCode(const uint8_t* code, size_t numBytes) {
if (this->oom())
return false;
for (Slice* cur = other.head; cur != nullptr; cur = cur->getNext()) {
this->putBytes(cur->length(), &cur->instructions);
if (this->oom())
return false;
}
return true;
this->putBytes(numBytes, code);
return !this->oom();
}
};
@ -958,7 +953,7 @@ class AssemblerMIPSShared : public AssemblerShared
bool isFinished;
public:
void finish();
bool asmMergeWith(const AssemblerMIPSShared& other);
bool appendRawCode(const uint8_t* code, size_t numBytes);
void executableCopy(void* buffer, bool flushICache = true);
void copyJumpRelocationTable(uint8_t* dest);
void copyDataRelocationTable(uint8_t* dest);
@ -1233,7 +1228,7 @@ class AssemblerMIPSShared : public AssemblerShared
void bind(Label* label, BufferOffset boff = BufferOffset());
void bindLater(Label* label, wasm::TrapDesc target);
virtual void bind(InstImm* inst, uintptr_t branch, uintptr_t target) = 0;
virtual void Bind(uint8_t* rawCode, CodeOffset* label, const void* address) = 0;
virtual void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target) = 0;
void bind(CodeOffset* label) {
label->bind(currentOffset());
}
@ -1245,9 +1240,6 @@ class AssemblerMIPSShared : public AssemblerShared
}
void retarget(Label* label, Label* target);
// See Bind
size_t labelToPatchOffset(CodeOffset label) { return label.offset(); }
void call(Label* label);
void call(void* target);

View File

@ -302,12 +302,12 @@ Assembler::trace(JSTracer* trc)
}
void
Assembler::Bind(uint8_t* rawCode, CodeOffset* label, const void* address)
Assembler::Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target)
{
if (label->bound()) {
intptr_t offset = label->offset();
if (label.bound()) {
intptr_t offset = label.offset();
Instruction* inst = (Instruction*) (rawCode + offset);
AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), (uint32_t)address);
AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), (uint32_t)(rawCode + target.offset()));
}
}
@ -499,13 +499,6 @@ Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newVal
AutoFlushICache::flush(uintptr_t(inst), 8);
}
void
Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm)
{
InstImm* inst = (InstImm*)code;
AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), (uint32_t)imm.value);
}
uint32_t
Assembler::ExtractInstructionImmediate(uint8_t* code)
{

View File

@ -155,7 +155,7 @@ class Assembler : public AssemblerMIPSShared
using AssemblerMIPSShared::bind;
void bind(RepatchLabel* label);
void Bind(uint8_t* rawCode, CodeOffset* label, const void* address);
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target);
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
@ -178,7 +178,6 @@ class Assembler : public AssemblerMIPSShared
static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
PatchedImmPtr expectedValue);
static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
static uint32_t ExtractInstructionImmediate(uint8_t* code);
static void ToggleCall(CodeLocationLabel inst_, bool enabled);

View File

@ -235,12 +235,12 @@ Assembler::trace(JSTracer* trc)
}
void
Assembler::Bind(uint8_t* rawCode, CodeOffset* label, const void* address)
Assembler::Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target)
{
if (label->bound()) {
intptr_t offset = label->offset();
if (label.bound()) {
intptr_t offset = label.offset();
Instruction* inst = (Instruction*) (rawCode + offset);
Assembler::UpdateLoad64Value(inst, (uint64_t)address);
Assembler::UpdateLoad64Value(inst, (uint64_t)(rawCode + target.offset()));
}
}
@ -490,13 +490,6 @@ Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newVal
AutoFlushICache::flush(uintptr_t(inst), 6 * sizeof(uint32_t));
}
void
Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm)
{
InstImm* inst = (InstImm*)code;
Assembler::UpdateLoad64Value(inst, (uint64_t)imm.value);
}
uint64_t
Assembler::ExtractInstructionImmediate(uint8_t* code)
{

View File

@ -147,7 +147,7 @@ class Assembler : public AssemblerMIPSShared
using AssemblerMIPSShared::bind;
void bind(RepatchLabel* label);
void Bind(uint8_t* rawCode, CodeOffset* label, const void* address);
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target);
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
@ -171,7 +171,6 @@ class Assembler : public AssemblerMIPSShared
static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
PatchedImmPtr expectedValue);
static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
static uint64_t ExtractInstructionImmediate(uint8_t* code);
static void ToggleCall(CodeLocationLabel inst_, bool enabled);

View File

@ -145,7 +145,6 @@ class Assembler : public AssemblerShared
static void PatchWrite_NearCall(CodeLocationLabel, CodeLocationLabel) { MOZ_CRASH(); }
static uint32_t PatchWrite_NearCallSize() { MOZ_CRASH(); }
static void PatchInstructionImmediate(uint8_t*, PatchedImmPtr) { MOZ_CRASH(); }
static void ToggleToJmp(CodeLocationLabel) { MOZ_CRASH(); }
static void ToggleToCmp(CodeLocationLabel) { MOZ_CRASH(); }
@ -188,7 +187,7 @@ class MacroAssemblerNone : public Assembler
size_t numCodeLabels() const { MOZ_CRASH(); }
CodeLabel codeLabel(size_t) { MOZ_CRASH(); }
bool asmMergeWith(const MacroAssemblerNone&) { MOZ_CRASH(); }
bool appendRawCode(const uint8_t* code, size_t numBytes) { MOZ_CRASH(); }
void trace(JSTracer*) { MOZ_CRASH(); }
static void TraceJumpRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
@ -215,7 +214,6 @@ class MacroAssemblerNone : public Assembler
void nopAlign(size_t) { MOZ_CRASH(); }
void checkStackAlignment() { MOZ_CRASH(); }
uint32_t currentOffset() { MOZ_CRASH(); }
uint32_t labelToPatchOffset(CodeOffset) { MOZ_CRASH(); }
CodeOffset labelForPatch() { MOZ_CRASH(); }
void nop() { MOZ_CRASH(); }

View File

@ -461,12 +461,16 @@ class CodeLabel
CodeOffset* target() {
return &target_;
}
void offsetBy(size_t delta) {
patchAt_.offsetBy(delta);
target_.offsetBy(delta);
CodeOffset patchAt() const {
return patchAt_;
}
CodeOffset target() const {
return target_;
}
};
typedef Vector<CodeLabel, 0, SystemAllocPolicy> CodeLabelVector;
// Location of a jump or label in a generated JitCode block, relative to the
// start of the block.
@ -807,15 +811,16 @@ namespace jit {
// The base class of all Assemblers for all archs.
class AssemblerShared
{
wasm::CallSiteAndTargetVector callSites_;
wasm::CallFarJumpVector callFarJumps_;
wasm::CallSiteVector callSites_;
wasm::CallSiteTargetVector callSiteTargets_;
wasm::TrapSiteVector trapSites_;
wasm::TrapFarJumpVector trapFarJumps_;
wasm::CallFarJumpVector callFarJumps_;
wasm::MemoryAccessVector memoryAccesses_;
wasm::SymbolicAccessVector symbolicAccesses_;
protected:
Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
CodeLabelVector codeLabels_;
bool enoughMemory_;
bool embedsNurseryPointers_;
@ -842,33 +847,43 @@ class AssemblerShared
return embedsNurseryPointers_;
}
static bool canUseInSingleByteInstruction(Register reg) {
return true;
}
void addCodeLabel(CodeLabel label) {
propagateOOM(codeLabels_.append(label));
}
size_t numCodeLabels() const {
return codeLabels_.length();
}
CodeLabel codeLabel(size_t i) {
return codeLabels_[i];
}
CodeLabelVector& codeLabels() {
return codeLabels_;
}
// WebAssembly metadata emitted by masm operations accumulated on the
// MacroAssembler, and swapped into a wasm::CompiledCode after finish().
template <typename... Args>
void append(const wasm::CallSiteDesc& desc, CodeOffset retAddr, Args&&... args)
{
wasm::CallSite cs(desc, retAddr.offset());
enoughMemory_ &= callSites_.emplaceBack(cs, mozilla::Forward<Args>(args)...);
void append(const wasm::CallSiteDesc& desc, CodeOffset retAddr, Args&&... args) {
enoughMemory_ &= callSites_.emplaceBack(desc, retAddr.offset());
enoughMemory_ &= callSiteTargets_.emplaceBack(mozilla::Forward<Args>(args)...);
}
wasm::CallSiteAndTargetVector& callSites() { return callSites_; }
void append(wasm::CallFarJump jmp) {
enoughMemory_ &= callFarJumps_.append(jmp);
}
const wasm::CallFarJumpVector& callFarJumps() const { return callFarJumps_; }
void append(wasm::TrapSite trapSite) {
enoughMemory_ &= trapSites_.append(trapSite);
}
const wasm::TrapSiteVector& trapSites() const { return trapSites_; }
void clearTrapSites() { trapSites_.clear(); }
void append(wasm::TrapFarJump jmp) {
enoughMemory_ &= trapFarJumps_.append(jmp);
}
const wasm::TrapFarJumpVector& trapFarJumps() const { return trapFarJumps_; }
void append(wasm::MemoryAccess access) { enoughMemory_ &= memoryAccesses_.append(access); }
wasm::MemoryAccessVector&& extractMemoryAccesses() { return Move(memoryAccesses_); }
void append(wasm::CallFarJump jmp) {
enoughMemory_ &= callFarJumps_.append(jmp);
}
void append(wasm::MemoryAccess access) {
enoughMemory_ &= memoryAccesses_.append(access);
}
void append(const wasm::MemoryAccessDesc& access, size_t codeOffset, size_t framePushed) {
if (access.hasTrap()) {
// If a memory access is trapping (wasm, SIMD.js, Atomics), create a
@ -887,60 +902,17 @@ class AssemblerShared
#endif
}
}
void append(wasm::SymbolicAccess access) { enoughMemory_ &= symbolicAccesses_.append(access); }
size_t numSymbolicAccesses() const { return symbolicAccesses_.length(); }
wasm::SymbolicAccess symbolicAccess(size_t i) const { return symbolicAccesses_[i]; }
static bool canUseInSingleByteInstruction(Register reg) { return true; }
void addCodeLabel(CodeLabel label) {
propagateOOM(codeLabels_.append(label));
}
size_t numCodeLabels() const {
return codeLabels_.length();
}
CodeLabel codeLabel(size_t i) {
return codeLabels_[i];
void append(wasm::SymbolicAccess access) {
enoughMemory_ &= symbolicAccesses_.append(access);
}
// Merge this assembler with the other one, invalidating it, by shifting all
// offsets by a delta.
bool asmMergeWith(size_t delta, const AssemblerShared& other) {
size_t i = callSites_.length();
enoughMemory_ &= callSites_.appendAll(other.callSites_);
for (; i < callSites_.length(); i++)
callSites_[i].offsetReturnAddressBy(delta);
MOZ_ASSERT(other.trapSites_.empty(), "should have been cleared by wasmEmitTrapOutOfLineCode");
i = callFarJumps_.length();
enoughMemory_ &= callFarJumps_.appendAll(other.callFarJumps_);
for (; i < callFarJumps_.length(); i++)
callFarJumps_[i].offsetBy(delta);
i = trapFarJumps_.length();
enoughMemory_ &= trapFarJumps_.appendAll(other.trapFarJumps_);
for (; i < trapFarJumps_.length(); i++)
trapFarJumps_[i].offsetBy(delta);
i = memoryAccesses_.length();
enoughMemory_ &= memoryAccesses_.appendAll(other.memoryAccesses_);
for (; i < memoryAccesses_.length(); i++)
memoryAccesses_[i].offsetBy(delta);
i = symbolicAccesses_.length();
enoughMemory_ &= symbolicAccesses_.appendAll(other.symbolicAccesses_);
for (; i < symbolicAccesses_.length(); i++)
symbolicAccesses_[i].patchAt.offsetBy(delta);
i = codeLabels_.length();
enoughMemory_ &= codeLabels_.appendAll(other.codeLabels_);
for (; i < codeLabels_.length(); i++)
codeLabels_[i].offsetBy(delta);
return !oom();
}
wasm::CallSiteVector& callSites() { return callSites_; }
wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; }
wasm::TrapSiteVector& trapSites() { return trapSites_; }
wasm::TrapFarJumpVector& trapFarJumps() { return trapFarJumps_; }
wasm::CallFarJumpVector& callFarJumps() { return callFarJumps_; }
wasm::MemoryAccessVector& memoryAccesses() { return memoryAccesses_; }
wasm::SymbolicAccessVector& symbolicAccesses() { return symbolicAccesses_; }
};
} // namespace jit

View File

@ -1126,17 +1126,18 @@ struct AssemblerBufferWithConstantPools : public AssemblerBuffer<SliceSize, Inst
}
}
bool appendBuffer(const AssemblerBufferWithConstantPools& other) {
bool appendRawCode(const uint8_t* code, size_t numBytes) {
if (this->oom())
return false;
// The pools should have all been flushed, check.
MOZ_ASSERT(pool_.numEntries() == 0);
for (Slice* cur = other.getHead(); cur != nullptr; cur = cur->getNext()) {
this->putBytes(cur->length(), &cur->instructions[0]);
if (this->oom())
return false;
while (numBytes > SliceSize) {
this->putBytes(SliceSize, code);
numBytes -= SliceSize;
code += SliceSize;
}
return true;
this->putBytes(numBytes, code);
return !this->oom();
}
public:

View File

@ -137,7 +137,7 @@ AssemblerX86Shared::processCodeLabels(uint8_t* rawCode)
{
for (size_t i = 0; i < codeLabels_.length(); i++) {
CodeLabel label = codeLabels_[i];
Bind(rawCode, label.patchAt(), rawCode + label.target()->offset());
Bind(rawCode, *label.patchAt(), *label.target());
}
}

View File

@ -403,6 +403,12 @@ class AssemblerX86Shared : public AssemblerShared
jumpRelocations_.oom() ||
dataRelocations_.oom();
}
bool reserve(size_t size) {
return masm.reserve(size);
}
bool swapBuffer(wasm::Bytes& other) {
return masm.swapBuffer(other);
}
void setPrinter(Sprinter* sp) {
masm.setPrinter(sp);
@ -413,12 +419,6 @@ class AssemblerX86Shared : public AssemblerShared
}
void executableCopy(void* buffer);
bool asmMergeWith(const AssemblerX86Shared& other) {
MOZ_ASSERT(other.jumps_.length() == 0);
if (!AssemblerShared::asmMergeWith(masm.size(), other))
return false;
return masm.appendBuffer(other.masm);
}
void processCodeLabels(uint8_t* rawCode);
void copyJumpRelocationTable(uint8_t* dest);
void copyDataRelocationTable(uint8_t* dest);
@ -1012,18 +1012,13 @@ class AssemblerX86Shared : public AssemblerShared
label->reset();
}
static void Bind(uint8_t* raw, CodeOffset* label, const void* address) {
if (label->bound()) {
intptr_t offset = label->offset();
X86Encoding::SetPointer(raw + offset, address);
static void Bind(uint8_t* raw, CodeOffset label, CodeOffset target) {
if (label.bound()) {
intptr_t offset = label.offset();
X86Encoding::SetPointer(raw + offset, raw + target.offset());
}
}
// See Bind and X86Encoding::setPointer.
size_t labelToPatchOffset(CodeOffset label) {
return label.offset() - sizeof(void*);
}
void ret() {
masm.ret();
}
@ -3656,10 +3651,6 @@ class AssemblerX86Shared : public AssemblerShared
PatchDataWithValueCheck(data, PatchedImmPtr(newData.value), PatchedImmPtr(expectedData.value));
}
static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm) {
MOZ_CRASH("Unused.");
}
static uint32_t NopSize() {
return 1;
}

View File

@ -10,6 +10,39 @@
#include "jsopcode.h"
using namespace js;
using namespace jit;
bool
AssemblerBuffer::swap(Vector<uint8_t, 0, SystemAllocPolicy>& bytes)
{
// For now, specialize to the one use case.
MOZ_ASSERT(bytes.empty());
if (m_buffer.empty()) {
if (bytes.capacity() > m_buffer.capacity()) {
size_t newCapacity = bytes.capacity();
uint8_t* newBuffer = bytes.extractRawBuffer();
MOZ_ASSERT(newBuffer);
m_buffer.replaceRawBuffer((unsigned char*)newBuffer, 0, newCapacity);
}
return true;
}
size_t newLength = m_buffer.length();
size_t newCapacity = m_buffer.capacity();
unsigned char* newBuffer = m_buffer.extractRawBuffer();
// NB: extractRawBuffer() only returns null if the Vector is using
// inline storage and thus a malloc would be needed. In this case,
// just make a simple copy.
if (!newBuffer)
return bytes.append(m_buffer.begin(), m_buffer.end());
bytes.replaceRawBuffer((uint8_t*)newBuffer, newLength, newCapacity);
return true;
}
#ifdef JS_JITSPEW
void
js::jit::GenericAssembler::spew(const char* fmt, va_list va)

View File

@ -125,6 +125,13 @@ namespace jit {
return m_oom;
}
bool reserve(size_t size)
{
return !m_oom && m_buffer.reserve(size);
}
bool swap(Vector<uint8_t, 0, SystemAllocPolicy>& bytes);
const unsigned char* buffer() const
{
MOZ_RELEASE_ASSERT(!m_oom);

View File

@ -57,6 +57,8 @@ public:
const unsigned char* buffer() const { return m_formatter.buffer(); }
unsigned char* data() { return m_formatter.data(); }
bool oom() const { return m_formatter.oom(); }
bool reserve(size_t size) { return m_formatter.reserve(size); }
bool swapBuffer(wasm::Bytes& other) { return m_formatter.swapBuffer(other); }
void nop()
{
@ -3873,11 +3875,9 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
const unsigned char* src = m_formatter.buffer();
memcpy(dst, src, size());
}
MOZ_MUST_USE bool appendBuffer(const BaseAssembler& other)
MOZ_MUST_USE bool appendRawCode(const uint8_t* code, size_t numBytes)
{
const unsigned char* buf = other.m_formatter.buffer();
bool ret = m_formatter.append(buf, other.size());
return ret;
return m_formatter.append(code, numBytes);
}
protected:
@ -5144,6 +5144,8 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
const unsigned char* buffer() const { return m_buffer.buffer(); }
unsigned char* data() { return m_buffer.data(); }
bool oom() const { return m_buffer.oom(); }
bool reserve(size_t size) { return m_buffer.reserve(size); }
bool swapBuffer(wasm::Bytes& other) { return m_buffer.swap(other); }
bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); }
MOZ_MUST_USE bool append(const unsigned char* values, size_t size)

View File

@ -271,50 +271,6 @@ MacroAssemblerX86Shared::getSimdData(const SimdConstant& v)
return getConstant<SimdData, SimdMap>(v, simdMap_, simds_);
}
template<class T, class Map>
static bool
MergeConstants(size_t delta, const Vector<T, 0, SystemAllocPolicy>& other,
Map& map, Vector<T, 0, SystemAllocPolicy>& vec)
{
typedef typename Map::AddPtr AddPtr;
if (!map.initialized() && !map.init())
return false;
for (const T& c : other) {
size_t index;
if (AddPtr p = map.lookupForAdd(c.value)) {
index = p->value();
} else {
index = vec.length();
if (!vec.append(T(c.value)) || !map.add(p, c.value, index))
return false;
}
MacroAssemblerX86Shared::UsesVector& uses = vec[index].uses;
for (CodeOffset use : c.uses) {
use.offsetBy(delta);
if (!uses.append(use))
return false;
}
}
return true;
}
bool
MacroAssemblerX86Shared::asmMergeWith(const MacroAssemblerX86Shared& other)
{
size_t sizeBefore = masm.size();
if (!Assembler::asmMergeWith(other))
return false;
if (!MergeConstants<Double, DoubleMap>(sizeBefore, other.doubles_, doubleMap_, doubles_))
return false;
if (!MergeConstants<Float, FloatMap>(sizeBefore, other.floats_, floatMap_, floats_))
return false;
if (!MergeConstants<SimdData, SimdMap>(sizeBefore, other.simds_, simdMap_, simds_))
return false;
return true;
}
void
MacroAssemblerX86Shared::minMaxDouble(FloatRegister first, FloatRegister second, bool canBeNaN,
bool isMax)

View File

@ -100,7 +100,9 @@ class MacroAssemblerX86Shared : public Assembler
MacroAssemblerX86Shared()
{ }
bool asmMergeWith(const MacroAssemblerX86Shared& other);
bool appendRawCode(const uint8_t* code, size_t numBytes) {
return masm.appendRawCode(code, numBytes);
}
// Evaluate srcDest = minmax<isMax>{Float32,Double}(srcDest, second).
// Checks for NaN if canBeNaN is true.

View File

@ -56,7 +56,7 @@ enum class ParseTaskKind
namespace wasm {
class CompileTask;
struct CompileTask;
typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
struct Tier2GeneratorTask

View File

@ -1786,7 +1786,7 @@ WasmActivation::interrupted() const
return false;
DebugOnly<wasm::Frame*> fp = act->asWasm()->exitFP();
MOZ_ASSERT(fp && fp->instance()->code().containsFunctionPC(pc));
MOZ_ASSERT(fp && fp->instance()->code().containsCodePC(pc));
return true;
}

View File

@ -1865,7 +1865,7 @@ class MOZ_STACK_CLASS ModuleValidator
return false;
}
return mg_.init(asmJSMetadata_.get());
return mg_.init(/* codeSectionSize (ignored) = */ 0, asmJSMetadata_.get());
}
JSContext* cx() const { return cx_; }

View File

@ -570,7 +570,7 @@ class BaseCompiler
const ModuleEnvironment& env_;
BaseOpIter iter_;
const FuncCompileUnit& func_;
const FuncCompileInput& func_;
size_t lastReadCallSite_;
TempAllocator& alloc_;
const ValTypeVector& locals_; // Types of parameters and locals
@ -645,7 +645,7 @@ class BaseCompiler
public:
BaseCompiler(const ModuleEnvironment& env,
Decoder& decoder,
const FuncCompileUnit& func,
const FuncCompileInput& input,
const ValTypeVector& locals,
bool debugEnabled,
TempAllocator* alloc,
@ -659,7 +659,7 @@ class BaseCompiler
MOZ_MUST_USE bool emitFunction();
void emitInitStackLocals();
const SigWithId& sig() const { return *env_.funcSigs[func_.index()]; }
const SigWithId& sig() const { return *env_.funcSigs[func_.index]; }
// Used by some of the ScratchRegister implementations.
operator MacroAssembler&() const { return masm; }
@ -2196,7 +2196,7 @@ class BaseCompiler
void insertBreakablePoint(CallSiteDesc::Kind kind) {
// The debug trap exit requires WasmTlsReg be loaded. However, since we
// are emitting millions of these breakable points inline, we push this
// loading of TLS into the FarJumpIsland created by patchCallSites.
// loading of TLS into the FarJumpIsland created by linkCallSites.
masm.nopPatchableToCall(CallSiteDesc(iter_.lastOpcodeOffset(), kind));
}
@ -2207,9 +2207,9 @@ class BaseCompiler
void beginFunction() {
JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code");
SigIdDesc sigId = env_.funcSigs[func_.index()]->id;
SigIdDesc sigId = env_.funcSigs[func_.index]->id;
if (mode_ == CompileMode::Tier1)
GenerateFunctionPrologue(masm, localSize_, sigId, &offsets_, mode_, func_.index());
GenerateFunctionPrologue(masm, localSize_, sigId, &offsets_, mode_, func_.index);
else
GenerateFunctionPrologue(masm, localSize_, sigId, &offsets_);
@ -2220,7 +2220,7 @@ class BaseCompiler
if (debugEnabled_) {
// Initialize funcIndex and flag fields of DebugFrame.
size_t debugFrame = masm.framePushed() - DebugFrame::offsetOfFrame();
masm.store32(Imm32(func_.index()),
masm.store32(Imm32(func_.index),
Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFuncIndex()));
masm.storePtr(ImmWord(0),
Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFlagsWord()));
@ -2345,7 +2345,7 @@ class BaseCompiler
MOZ_ASSERT(localSize_ >= debugFrameReserved);
if (localSize_ > debugFrameReserved)
masm.addToStackPtr(Imm32(localSize_ - debugFrameReserved));
BytecodeOffset prologueTrapOffset(func_.lineOrBytecode());
BytecodeOffset prologueTrapOffset(func_.lineOrBytecode);
masm.jump(TrapDesc(prologueTrapOffset, Trap::StackOverflow, debugFrameReserved));
masm.bind(&returnLabel_);
@ -3663,8 +3663,8 @@ class BaseCompiler
// Sundry helpers.
uint32_t readCallSiteLineOrBytecode() {
if (!func_.callSiteLineNums().empty())
return func_.callSiteLineNums()[lastReadCallSite_++];
if (!func_.callSiteLineNums.empty())
return func_.callSiteLineNums[lastReadCallSite_++];
return iter_.lastOpcodeOffset();
}
@ -7586,7 +7586,7 @@ BaseCompiler::emitInitStackLocals()
BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
Decoder& decoder,
const FuncCompileUnit& func,
const FuncCompileInput& func,
const ValTypeVector& locals,
bool debugEnabled,
TempAllocator* alloc,
@ -7707,7 +7707,7 @@ FuncOffsets
BaseCompiler::finish()
{
MOZ_ASSERT(done(), "all bytes must be consumed");
MOZ_ASSERT(func_.callSiteLineNums().length() == lastReadCallSite_);
MOZ_ASSERT(func_.callSiteLineNums.length() == lastReadCallSite_);
masm.flushBuffer();
@ -7743,37 +7743,54 @@ js::wasm::BaselineCanCompile()
}
bool
js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* func, UniqueChars* error)
js::wasm::BaselineCompileFunctions(const ModuleEnvironment& env, LifoAlloc& lifo,
const FuncCompileInputVector& inputs, CompiledCode* code,
UniqueChars* error)
{
MOZ_ASSERT(task->tier() == Tier::Baseline);
MOZ_ASSERT(task->env().kind == ModuleKind::Wasm);
Decoder d(func->begin(), func->end(), func->lineOrBytecode(), error);
// Build the local types vector.
ValTypeVector locals;
if (!locals.appendAll(task->env().funcSigs[func->index()]->args()))
return false;
if (!DecodeLocalEntries(d, task->env().kind, &locals))
return false;
MOZ_ASSERT(env.tier() == Tier::Baseline);
MOZ_ASSERT(env.kind == ModuleKind::Wasm);
// The MacroAssembler will sometimes access the jitContext.
JitContext jitContext(&task->alloc());
TempAllocator alloc(&lifo);
JitContext jitContext(&alloc);
MOZ_ASSERT(IsCompilingWasm());
MacroAssembler masm(MacroAssembler::WasmToken(), alloc);
// One-pass baseline compilation.
BaseCompiler f(task->env(), d, *func, locals, task->debugEnabled(), &task->alloc(),
&task->masm(), task->mode());
if (!f.init())
// Swap in already-allocated empty vectors to avoid malloc/free.
MOZ_ASSERT(code->empty());
if (!code->swap(masm))
return false;
if (!f.emitFunction())
for (const FuncCompileInput& func : inputs) {
Decoder d(func.begin, func.end, func.lineOrBytecode, error);
// Build the local types vector.
ValTypeVector locals;
if (!locals.appendAll(env.funcSigs[func.index]->args()))
return false;
if (!DecodeLocalEntries(d, env.kind, &locals))
return false;
// One-pass baseline compilation.
BaseCompiler f(env, d, func, locals, env.debugEnabled(), &alloc, &masm, env.mode());
if (!f.init())
return false;
if (!f.emitFunction())
return false;
if (!code->codeRanges.emplaceBack(func.index, func.lineOrBytecode, f.finish()))
return false;
}
masm.finish();
if (masm.oom())
return false;
func->finish(f.finish());
return true;
return code->swap(masm);
}
#undef INT_DIV_I64_CALLOUT

View File

@ -20,22 +20,20 @@
#define asmjs_wasm_baseline_compile_h
#include "wasm/WasmGenerator.h"
#include "wasm/WasmTypes.h"
namespace js {
namespace wasm {
class CompileTask;
class FuncCompileUnit;
// Return whether BaselineCompileFunction can generate code on the current device.
// Note: asm.js is also currently not supported due to Atomics and SIMD.
bool
BaselineCanCompile();
// Generate adequate code quickly.
bool
BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error);
MOZ_MUST_USE bool
BaselineCompileFunctions(const ModuleEnvironment& env, LifoAlloc& lifo,
const FuncCompileInputVector& inputs, CompiledCode* code,
UniqueChars* error);
class BaseLocalIter
{

View File

@ -833,9 +833,13 @@ wasm::EnsureBuiltinThunksInitialized()
ABIFunctionType abiType;
void* funcPtr = AddressOf(sym, &abiType);
ExitReason exitReason(sym);
CallableOffsets offset = GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr);
if (masm.oom() || !thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offset))
CallableOffsets offsets;
if (!GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr, &offsets))
return false;
if (!thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offsets))
return false;
}
@ -855,9 +859,13 @@ wasm::EnsureBuiltinThunksInitialized()
ABIFunctionType abiType = typedNative.abiType;
void* funcPtr = r.front().value();
ExitReason exitReason = ExitReason::Fixed::BuiltinNative;
CallableOffsets offset = GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr);
if (masm.oom() || !thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offset))
CallableOffsets offsets;
if (!GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr, &offsets))
return false;
if (!thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offsets))
return false;
}
@ -878,11 +886,13 @@ wasm::EnsureBuiltinThunksInitialized()
masm.processCodeLabels(thunks->codeBase);
#ifdef DEBUG
MOZ_ASSERT(masm.callSites().empty());
MOZ_ASSERT(masm.callSiteTargets().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.trapSites().empty());
MOZ_ASSERT(masm.trapFarJumps().empty());
MOZ_ASSERT(masm.extractMemoryAccesses().empty());
MOZ_ASSERT(!masm.numSymbolicAccesses());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.memoryAccesses().empty());
MOZ_ASSERT(masm.symbolicAccesses().empty());
#endif
ExecutableAllocator::cacheFlush(thunks->codeBase, thunks->codeSize);

View File

@ -89,12 +89,9 @@ static bool
StaticallyLink(const CodeSegment& cs, const LinkDataTier& linkData)
{
for (LinkDataTier::InternalLink link : linkData.internalLinks) {
uint8_t* patchAt = cs.base() + link.patchAtOffset;
void* target = cs.base() + link.targetOffset;
if (link.isRawPointerPatch())
*(void**)(patchAt) = target;
else
Assembler::PatchInstructionImmediate(patchAt, PatchedImmPtr(target));
CodeOffset patchAt(link.patchAtOffset);
CodeOffset target(link.targetOffset);
Assembler::Bind(cs.base(), patchAt, target);
}
if (!EnsureBuiltinThunksInitialized())
@ -121,12 +118,9 @@ static void
StaticallyUnlink(uint8_t* base, const LinkDataTier& linkData)
{
for (LinkDataTier::InternalLink link : linkData.internalLinks) {
uint8_t* patchAt = base + link.patchAtOffset;
void* target = 0;
if (link.isRawPointerPatch())
*(void**)(patchAt) = target;
else
Assembler::PatchInstructionImmediate(patchAt, PatchedImmPtr(target));
CodeOffset patchAt(link.patchAtOffset);
CodeOffset target(-size_t(base)); // to reset immediate to null
Assembler::Bind(base, patchAt, target);
}
for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
@ -203,8 +197,6 @@ CodeSegment::create(Tier tier,
uint32_t padding = ComputeByteAlignment(bytesNeeded, gc::SystemPageSize());
uint32_t codeLength = bytesNeeded + padding;
MOZ_ASSERT(linkData.functionCodeLength < codeLength);
UniqueCodeBytes codeBytes = AllocateCodeBytes(codeLength);
if (!codeBytes)
return nullptr;
@ -272,10 +264,12 @@ CodeSegment::initialize(Tier tier,
const Metadata& metadata)
{
MOZ_ASSERT(bytes_ == nullptr);
MOZ_ASSERT(linkData.interruptOffset);
MOZ_ASSERT(linkData.outOfBoundsOffset);
MOZ_ASSERT(linkData.unalignedAccessOffset);
tier_ = tier;
bytes_ = Move(codeBytes);
functionLength_ = linkData.functionCodeLength;
length_ = codeLength;
interruptCode_ = bytes_.get() + linkData.interruptOffset;
outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
@ -565,8 +559,7 @@ Metadata::serializedSize() const
SerializedPodVectorSize(tables) +
SerializedPodVectorSize(funcNames) +
SerializedPodVectorSize(customSections) +
filename.serializedSize() +
sizeof(hash);
filename.serializedSize();
}
size_t
@ -598,7 +591,6 @@ Metadata::serialize(uint8_t* cursor) const
cursor = SerializePodVector(cursor, funcNames);
cursor = SerializePodVector(cursor, customSections);
cursor = filename.serialize(cursor);
cursor = WriteBytes(cursor, hash, sizeof(hash));
return cursor;
}
@ -612,8 +604,7 @@ Metadata::deserialize(const uint8_t* cursor)
(cursor = DeserializePodVector(cursor, &tables)) &&
(cursor = DeserializePodVector(cursor, &funcNames)) &&
(cursor = DeserializePodVector(cursor, &customSections)) &&
(cursor = filename.deserialize(cursor)) &&
(cursor = ReadBytes(cursor, hash, sizeof(hash)));
(cursor = filename.deserialize(cursor));
debugEnabled = false;
debugFuncArgTypes.clear();
debugFuncReturnTypes.clear();
@ -632,8 +623,8 @@ struct ProjectFuncIndex
}
};
const FuncExport&
MetadataTier::lookupFuncExport(uint32_t funcIndex) const
FuncExport&
MetadataTier::lookupFuncExport(uint32_t funcIndex)
{
size_t match;
if (!BinarySearch(ProjectFuncIndex(funcExports), 0, funcExports.length(), funcIndex, &match))
@ -642,6 +633,12 @@ MetadataTier::lookupFuncExport(uint32_t funcIndex) const
return funcExports[match];
}
const FuncExport&
MetadataTier::lookupFuncExport(uint32_t funcIndex) const
{
return const_cast<MetadataTier*>(this)->lookupFuncExport(funcIndex);
}
bool
Metadata::getFuncName(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const
{
@ -739,20 +736,6 @@ Code::segment(Tier tier) const
}
}
bool
Code::containsFunctionPC(const void* pc, const CodeSegment** segmentp) const
{
for (auto t : tiers()) {
const CodeSegment& cs = segment(t);
if (cs.containsFunctionPC(pc)) {
if (segmentp)
*segmentp = &cs;
return true;
}
}
return false;
}
bool
Code::containsCodePC(const void* pc, const CodeSegment** segmentp) const
{
@ -875,7 +858,7 @@ Code::lookupMemoryAccess(void* pc, const CodeSegment** segmentp) const
if (BinarySearch(MemoryAccessOffset(memoryAccesses), lowerBound, upperBound, target,
&match))
{
MOZ_ASSERT(segment(t).containsFunctionPC(pc));
MOZ_ASSERT(segment(t).containsCodePC(pc));
if (segmentp)
*segmentp = &segment(t);
return &memoryAccesses[match];

View File

@ -69,21 +69,15 @@ class CodeSegment
typedef UniquePtr<uint8_t, FreeCode> UniqueCodeBytes;
static UniqueCodeBytes AllocateCodeBytes(uint32_t codeLength);
// How this code was compiled.
Tier tier_;
// bytes_ points to a single allocation of executable machine code in
// the range [0, length_). The range [0, functionLength_) is
// the subrange of [0, length_) which contains function code.
Tier tier_;
UniqueCodeBytes bytes_;
uint32_t functionLength_;
uint32_t length_;
// These are pointers into code for stubs used for asynchronous
// signal-handler control-flow transfer.
uint8_t* interruptCode_;
uint8_t* outOfBoundsCode_;
uint8_t* unalignedAccessCode_;
uint8_t* interruptCode_;
uint8_t* outOfBoundsCode_;
uint8_t* unalignedAccessCode_;
bool initialize(Tier tier,
UniqueCodeBytes bytes,
@ -104,7 +98,6 @@ class CodeSegment
CodeSegment()
: tier_(Tier(-1)),
functionLength_(0),
length_(0),
interruptCode_(nullptr),
outOfBoundsCode_(nullptr),
@ -132,15 +125,6 @@ class CodeSegment
uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
// The range [0, functionBytes) is a subrange of [0, codeBytes) that
// contains only function body code, not the stub code. This distinction is
// used by the async interrupt handler to only interrupt when the pc is in
// function code which, in turn, simplifies reasoning about how stubs
// enter/exit.
bool containsFunctionPC(const void* pc) const {
return pc >= base() && pc < (base() + functionLength_);
}
bool containsCodePC(const void* pc) const {
return pc >= base() && pc < (base() + length_);
}
@ -173,19 +157,21 @@ class FuncExport
public:
FuncExport() = default;
explicit FuncExport(Sig&& sig,
uint32_t funcIndex,
uint32_t codeRangeIndex)
explicit FuncExport(Sig&& sig, uint32_t funcIndex)
: sig_(Move(sig))
{
pod.funcIndex_ = funcIndex;
pod.codeRangeIndex_ = codeRangeIndex;
pod.codeRangeIndex_ = UINT32_MAX;
pod.entryOffset_ = UINT32_MAX;
}
void initEntryOffset(uint32_t entryOffset) {
MOZ_ASSERT(pod.entryOffset_ == UINT32_MAX);
pod.entryOffset_ = entryOffset;
}
void initCodeRangeIndex(uint32_t codeRangeIndex) {
MOZ_ASSERT(pod.codeRangeIndex_ == UINT32_MAX);
pod.codeRangeIndex_ = codeRangeIndex;
}
const Sig& sig() const {
return sig_;
@ -194,6 +180,7 @@ class FuncExport
return pod.funcIndex_;
}
uint32_t codeRangeIndex() const {
MOZ_ASSERT(pod.codeRangeIndex_ != UINT32_MAX);
return pod.codeRangeIndex_;
}
uint32_t entryOffset() const {
@ -362,6 +349,7 @@ struct MetadataTier
Uint32Vector debugTrapFarJumpOffsets;
Uint32Vector debugFuncToCodeRange;
FuncExport& lookupFuncExport(uint32_t funcIndex);
const FuncExport& lookupFuncExport(uint32_t funcIndex) const;
WASM_DECLARE_SERIALIZABLE(MetadataTier);
@ -379,7 +367,8 @@ class Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod
explicit Metadata(UniqueMetadataTier tier, ModuleKind kind = ModuleKind::Wasm)
: MetadataCacheablePod(kind),
metadata1_(Move(tier)),
debugEnabled(false)
debugEnabled(false),
debugHash()
{}
virtual ~Metadata() {}
@ -406,12 +395,12 @@ class Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod
NameInBytecodeVector funcNames;
CustomSectionVector customSections;
CacheableChars filename;
ModuleHash hash;
// Debug-enabled code is not serialized.
bool debugEnabled;
FuncArgTypesVector debugFuncArgTypes;
FuncReturnTypesVector debugFuncReturnTypes;
ModuleHash debugHash;
bool usesMemory() const { return UsesMemory(memoryUsage); }
bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
@ -478,15 +467,11 @@ class Code : public ShareableBase<Code>
const MetadataTier& metadata(Tier tier) const { return metadata_->metadata(tier); }
const Metadata& metadata() const { return *metadata_; }
// Frame iterator support:
// Metadata lookup functions:
const CallSite* lookupCallSite(void* returnAddress, const CodeSegment** segment = nullptr) const;
const CodeRange* lookupRange(void* pc, const CodeSegment** segment = nullptr) const;
const MemoryAccess* lookupMemoryAccess(void* pc, const CodeSegment** segment = nullptr) const;
// Signal handling support:
bool containsFunctionPC(const void* pc, const CodeSegment** segmentp = nullptr) const;
bool containsCodePC(const void* pc, const CodeSegment** segmentp = nullptr) const;
// To save memory, profilingLabels_ are generated lazily when profiling mode

View File

@ -23,6 +23,7 @@
#include "jsprf.h"
#include "jit/ProcessExecutableMemory.h"
#include "wasm/WasmBaselineCompile.h"
#include "wasm/WasmBinaryIterator.h"
#include "wasm/WasmGenerator.h"
@ -103,10 +104,286 @@ CompileArgs::initFromContext(JSContext* cx, ScriptedCaller&& scriptedCaller)
return assumptions.initBuildIdFromContext(cx);
}
static bool
BackgroundWorkPossible()
// Classify the current system as one of a set of recognizable classes. This
// really needs to get our tier-1 systems right.
//
// TODO: We don't yet have a good measure of how fast a system is. We
// distinguish between mobile and desktop because these are very different kinds
// of systems, but we could further distinguish between low / medium / high end
// within those major classes. If we do so, then constants below would be
// provided for each (class, architecture, system-tier) combination, not just
// (class, architecture) as now.
//
// CPU clock speed is not by itself a good predictor of system performance, as
// there are high-performance systems with slow clocks (recent Intel) and
// low-performance systems with fast clocks (older AMD). We can also use
// physical memory, core configuration, OS details, CPU class and family, and
// CPU manufacturer to disambiguate.
enum class SystemClass
{
return CanUseExtraThreads() && HelperThreadState().cpuCount > 1;
DesktopX86,
DesktopX64,
DesktopUnknown32,
DesktopUnknown64,
MobileX86,
MobileArm32,
MobileArm64,
MobileUnknown32,
MobileUnknown64
};
static SystemClass
ClassifySystem()
{
bool isDesktop;
#if defined(ANDROID) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
isDesktop = false;
#else
isDesktop = true;
#endif
if (isDesktop) {
#if defined(JS_CODEGEN_X64)
return SystemClass::DesktopX64;
#elif defined(JS_CODEGEN_X86)
return SystemClass::DesktopX86;
#elif defined(JS_64BIT)
return SystemClass::DesktopUnknown64;
#else
return SystemClass::DesktopUnknown32;
#endif
} else {
#if defined(JS_CODEGEN_X86)
return SystemClass::MobileX86;
#elif defined(JS_CODEGEN_ARM)
return SystemClass::MobileArm32;
#elif defined(JS_CODEGEN_ARM64)
return SystemClass::MobileArm64;
#elif defined(JS_64BIT)
return SystemClass::MobileUnknown64;
#else
return SystemClass::MobileUnknown32;
#endif
}
}
// Code sizes in machine code bytes per bytecode byte, again empirical except
// where marked as "Guess".
static const double x64Tox86Inflation = 1.25;
static const double x64IonBytesPerBytecode = 2.45;
static const double x86IonBytesPerBytecode = x64IonBytesPerBytecode * x64Tox86Inflation;
static const double arm32IonBytesPerBytecode = 3.3;
static const double arm64IonBytesPerBytecode = 3.0; // Guess
static const double x64BaselineBytesPerBytecode = x64IonBytesPerBytecode * 1.43;
static const double x86BaselineBytesPerBytecode = x64BaselineBytesPerBytecode * x64Tox86Inflation;
static const double arm32BaselineBytesPerBytecode = arm32IonBytesPerBytecode * 1.39;
static const double arm64BaselineBytesPerBytecode = arm64IonBytesPerBytecode * 1.39; // Guess
static double
IonBytesPerBytecode(SystemClass cls)
{
switch (cls) {
case SystemClass::DesktopX86:
case SystemClass::MobileX86:
case SystemClass::DesktopUnknown32:
return x86IonBytesPerBytecode;
case SystemClass::DesktopX64:
case SystemClass::DesktopUnknown64:
return x64IonBytesPerBytecode;
case SystemClass::MobileArm32:
case SystemClass::MobileUnknown32:
return arm32IonBytesPerBytecode;
case SystemClass::MobileArm64:
case SystemClass::MobileUnknown64:
return arm64IonBytesPerBytecode;
default:
MOZ_CRASH();
}
}
static double
BaselineBytesPerBytecode(SystemClass cls)
{
switch (cls) {
case SystemClass::DesktopX86:
case SystemClass::MobileX86:
case SystemClass::DesktopUnknown32:
return x86BaselineBytesPerBytecode;
case SystemClass::DesktopX64:
case SystemClass::DesktopUnknown64:
return x64BaselineBytesPerBytecode;
case SystemClass::MobileArm32:
case SystemClass::MobileUnknown32:
return arm32BaselineBytesPerBytecode;
case SystemClass::MobileArm64:
case SystemClass::MobileUnknown64:
return arm64BaselineBytesPerBytecode;
default:
MOZ_CRASH();
}
}
double
wasm::EstimateCompiledCodeSize(Tier tier, size_t bytecodeSize)
{
SystemClass cls = ClassifySystem();
switch (tier) {
case Tier::Baseline:
return double(bytecodeSize) * BaselineBytesPerBytecode(cls);
case Tier::Ion:
return double(bytecodeSize) * IonBytesPerBytecode(cls);
}
MOZ_CRASH("bad tier");
}
// If parallel Ion compilation is going to take longer than this, we should tier.
static const double tierCutoffMs = 250;
// Compilation rate values are empirical except when noted, the reference
// systems are:
//
// Late-2013 MacBook Pro (2.6GHz quad hyperthreaded Haswell)
// Late-2015 Nexus 5X (1.4GHz quad Cortex-A53 + 1.8GHz dual Cortex-A57)
static const double x64BytecodesPerMs = 2100;
static const double x86BytecodesPerMs = 1500;
static const double arm32BytecodesPerMs = 450;
static const double arm64BytecodesPerMs = 650; // Guess
// Tiering cutoff values: if code section sizes are below these values (when
// divided by the effective number of cores) we do not tier, because we guess
// that parallel Ion compilation will be fast enough.
static const double x64DesktopTierCutoff = x64BytecodesPerMs * tierCutoffMs;
static const double x86DesktopTierCutoff = x86BytecodesPerMs * tierCutoffMs;
static const double x86MobileTierCutoff = x86DesktopTierCutoff / 2; // Guess
static const double arm32MobileTierCutoff = arm32BytecodesPerMs * tierCutoffMs;
static const double arm64MobileTierCutoff = arm64BytecodesPerMs * tierCutoffMs;
static double
CodesizeCutoff(SystemClass cls, uint32_t codeSize)
{
switch (cls) {
case SystemClass::DesktopX86:
case SystemClass::DesktopUnknown32:
return x86DesktopTierCutoff;
case SystemClass::DesktopX64:
case SystemClass::DesktopUnknown64:
return x64DesktopTierCutoff;
case SystemClass::MobileX86:
return x86MobileTierCutoff;
case SystemClass::MobileArm32:
case SystemClass::MobileUnknown32:
return arm32MobileTierCutoff;
case SystemClass::MobileArm64:
case SystemClass::MobileUnknown64:
return arm64MobileTierCutoff;
default:
MOZ_CRASH();
}
}
// As the number of cores grows the effectiveness of each core dwindles (on the
// systems we care about for SpiderMonkey).
//
// The data are empirical, computed from the observed compilation time of the
// Tanks demo code on a variable number of cores.
//
// The heuristic may fail on NUMA systems where the core count is high but the
// performance increase is nil or negative once the program moves beyond one
// socket. However, few browser users have such systems.
static double
EffectiveCores(SystemClass cls, uint32_t cores)
{
if (cores <= 3)
return pow(cores, 0.9);
return pow(cores, 0.75);
}
#ifndef JS_64BIT
// Don't tier if tiering will fill code memory to more to more than this
// fraction.
static const double spaceCutoffPct = 0.9;
#endif
// Figure out whether we should use tiered compilation or not.
static bool
GetTieringEnabled(uint32_t codeSize)
{
if (!CanUseExtraThreads())
return false;
uint32_t cpuCount = HelperThreadState().cpuCount;
MOZ_ASSERT(cpuCount > 0);
// It's mostly sensible not to background compile when there's only one
// hardware thread as we want foreground computation to have access to that.
// However, if wasm background compilation helper threads can be given lower
// priority then background compilation on single-core systems still makes
// some kind of sense. That said, this is a non-issue: as of September 2017
// 1-core was down to 3.5% of our population and falling.
if (cpuCount == 1)
return false;
MOZ_ASSERT(HelperThreadState().threadCount >= cpuCount);
// Compute the max number of threads available to do actual background
// compilation work.
uint32_t workers = HelperThreadState().maxWasmCompilationThreads();
// The number of cores we will use is bounded both by the CPU count and the
// worker count.
uint32_t cores = Min(cpuCount, workers);
SystemClass cls = ClassifySystem();
// Ion compilation on available cores must take long enough to be worth the
// bother.
double cutoffSize = CodesizeCutoff(cls, codeSize);
double effectiveCores = EffectiveCores(cls, cores);
if ((codeSize / effectiveCores) < cutoffSize)
return false;
// Do not implement a size cutoff for 64-bit systems since the code size
// budget for 64 bit is so large that it will hardly ever be an issue.
// (Also the cutoff percentage might be different on 64-bit.)
#ifndef JS_64BIT
// If the amount of executable code for baseline compilation jeopardizes the
// availability of executable memory for ion code then do not tier, for now.
//
// TODO: For now we consider this module in isolation. We should really
// worry about what else is going on in this process and might be filling up
// the code memory. It's like we need some kind of code memory reservation
// system or JIT compilation for large modules.
double ionRatio = IonBytesPerBytecode(cls);
double baselineRatio = BaselineBytesPerBytecode(cls);
double needMemory = codeSize * (ionRatio + baselineRatio);
double availMemory = LikelyAvailableExecutableMemory();
double cutoff = spaceCutoffPct * MaxCodeBytesPerProcess;
// If the sum of baseline and ion code makes us exceeds some set percentage
// of the executable memory then disable tiering.
if ((MaxCodeBytesPerProcess - availMemory) + needMemory > cutoff)
return false;
#endif
return true;
}
SharedModule
@ -118,27 +395,32 @@ wasm::CompileInitialTier(const ShareableBytes& bytecode, const CompileArgs& args
bool debugEnabled = BaselineCanCompile() && args.debugEnabled;
bool ionEnabled = args.ionEnabled || !baselineEnabled;
CompileMode mode;
Tier tier;
DebugEnabled debug;
if (BackgroundWorkPossible() && baselineEnabled && ionEnabled && !debugEnabled) {
mode = CompileMode::Tier1;
tier = Tier::Baseline;
debug = DebugEnabled::False;
} else {
mode = CompileMode::Once;
tier = debugEnabled || !ionEnabled ? Tier::Baseline : Tier::Ion;
debug = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
}
DebugEnabled debug = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
ModuleEnvironment env(mode, tier, debug);
ModuleEnvironment env(ModuleEnvironment::UnknownMode, ModuleEnvironment::UnknownTier, debug);
Decoder d(bytecode.bytes, error);
if (!DecodeModuleEnvironment(d, &env))
return nullptr;
uint32_t codeSize;
if (!d.peekSectionSize(SectionId::Code, &env, "code", &codeSize))
codeSize = 0;
CompileMode mode;
Tier tier;
if (baselineEnabled && ionEnabled && !debugEnabled && GetTieringEnabled(codeSize)) {
mode = CompileMode::Tier1;
tier = Tier::Baseline;
} else {
mode = CompileMode::Once;
tier = debugEnabled || !ionEnabled ? Tier::Baseline : Tier::Ion;
}
env.setModeAndTier(mode, tier);
ModuleGenerator mg(args, &env, nullptr, error);
if (!mg.init())
if (!mg.init(bytecode.length()))
return nullptr;
if (!DecodeCodeSection(d, mg, &env))
@ -163,7 +445,7 @@ wasm::CompileTier2(Module& module, const CompileArgs& args, Atomic<bool>* cancel
return false;
ModuleGenerator mg(args, &env, cancelled, &error);
if (!mg.init())
if (!mg.init(module.bytecode().length()))
return false;
if (!DecodeCodeSection(d, mg, &env))

View File

@ -60,6 +60,12 @@ struct CompileArgs : ShareableBase<CompileArgs>
typedef RefPtr<CompileArgs> MutableCompileArgs;
typedef RefPtr<const CompileArgs> SharedCompileArgs;
// Return the estimated compiled (machine) code size for the given bytecode size
// compiled at the given tier.
double
EstimateCompiledCodeSize(Tier tier, size_t bytecodeSize);
// Compile the given WebAssembly bytecode with the given arguments into a
// wasm::Module. On success, the Module is returned. On failure, the returned
// SharedModule pointer is null and either:

View File

@ -612,9 +612,11 @@ DebugState::debugDisplayURL(JSContext* cx) const
// - "wasm:" as protocol;
// - URI encoded filename from metadata (if can be encoded), plus ":";
// - 64-bit hash of the module bytes (as hex dump).
js::StringBuffer result(cx);
if (!result.append("wasm:"))
return nullptr;
if (const char* filename = metadata().filename.get()) {
js::StringBuffer filenamePrefix(cx);
// EncodeURI returns false due to invalid chars or OOM -- fail only
@ -623,19 +625,25 @@ DebugState::debugDisplayURL(JSContext* cx) const
if (!cx->isExceptionPending())
return nullptr;
cx->clearPendingException(); // ignore invalid URI
} else if (!result.append(filenamePrefix.finishString()) || !result.append(":")) {
} else if (!result.append(filenamePrefix.finishString())) {
return nullptr;
}
}
const ModuleHash& hash = metadata().hash;
for (size_t i = 0; i < sizeof(ModuleHash); i++) {
char digit1 = hash[i] / 16, digit2 = hash[i] % 16;
if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10)))
return nullptr;
if (!result.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10)))
if (metadata().debugEnabled) {
if (!result.append(":"))
return nullptr;
const ModuleHash& hash = metadata().debugHash;
for (size_t i = 0; i < sizeof(ModuleHash); i++) {
char digit1 = hash[i] / 16, digit2 = hash[i] % 16;
if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10)))
return nullptr;
if (!result.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10)))
return nullptr;
}
}
return result.finishString();
}

View File

@ -606,7 +606,8 @@ ProfilingFrameIterator::initFromExitFP()
case CodeRange::BuiltinThunk:
case CodeRange::TrapExit:
case CodeRange::DebugTrap:
case CodeRange::Inline:
case CodeRange::OutOfBoundsExit:
case CodeRange::UnalignedExit:
case CodeRange::Throw:
case CodeRange::Interrupt:
case CodeRange::FarJumpIsland:
@ -678,6 +679,7 @@ js::wasm::StartUnwinding(const WasmActivation& activation, const RegisterState&
case CodeRange::ImportInterpExit:
case CodeRange::BuiltinThunk:
case CodeRange::TrapExit:
case CodeRange::DebugTrap:
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
if (offsetFromEntry == BeforePushRetAddr || codeRange->isThunk()) {
// The return address is still in lr and fp holds the caller's fp.
@ -737,9 +739,9 @@ js::wasm::StartUnwinding(const WasmActivation& activation, const RegisterState&
break;
}
break;
case CodeRange::DebugTrap:
case CodeRange::Inline:
// Inline code stubs execute after the prologue/epilogue have completed
case CodeRange::OutOfBoundsExit:
case CodeRange::UnalignedExit:
// These code stubs execute after the prologue/epilogue have completed
// so pc/fp contains the right values here.
fixedPC = pc;
fixedFP = fp;
@ -846,7 +848,8 @@ ProfilingFrameIterator::operator++()
case CodeRange::BuiltinThunk:
case CodeRange::TrapExit:
case CodeRange::DebugTrap:
case CodeRange::Inline:
case CodeRange::OutOfBoundsExit:
case CodeRange::UnalignedExit:
case CodeRange::FarJumpIsland:
stackAddress_ = callerFP_;
callerPC_ = callerFP_->returnAddress;
@ -1014,7 +1017,8 @@ ProfilingFrameIterator::label() const
case CodeRange::ImportInterpExit: return importInterpDescription;
case CodeRange::TrapExit: return trapDescription;
case CodeRange::DebugTrap: return debugTrapDescription;
case CodeRange::Inline: return "inline stub (in wasm)";
case CodeRange::OutOfBoundsExit: return "out-of-bounds stub (in wasm)";
case CodeRange::UnalignedExit: return "unaligned trap stub (in wasm)";
case CodeRange::FarJumpIsland: return "interstitial (in wasm)";
case CodeRange::Throw: MOZ_FALLTHROUGH;
case CodeRange::Interrupt: MOZ_CRASH("does not have a frame");

File diff suppressed because it is too large Load Diff

View File

@ -27,55 +27,79 @@
namespace js {
namespace wasm {
struct CompileArgs;
struct ModuleEnvironment;
// FuncCompileInput contains the input for compiling a single function.
// FuncCompileUnit contains all the data necessary to produce and store the
// results of a single function's compilation.
class FuncCompileUnit
struct FuncCompileInput
{
// Input:
Bytes bytesToDelete_;
const uint8_t* begin_;
const uint8_t* end_;
uint32_t index_;
uint32_t lineOrBytecode_;
Uint32Vector callSiteLineNums_;
Bytes bytesToDelete;
const uint8_t* begin;
const uint8_t* end;
uint32_t index;
uint32_t lineOrBytecode;
Uint32Vector callSiteLineNums;
// Output:
FuncOffsets offsets_;
DebugOnly<bool> finished_;
public:
explicit FuncCompileUnit(uint32_t index, uint32_t lineOrBytecode,
Bytes&& bytesToDelete, const uint8_t* begin, const uint8_t* end,
Uint32Vector&& callSiteLineNums)
: bytesToDelete_(Move(bytesToDelete)),
begin_(begin),
end_(end),
index_(index),
lineOrBytecode_(lineOrBytecode),
callSiteLineNums_(Move(callSiteLineNums)),
finished_(false)
FuncCompileInput(uint32_t index,
uint32_t lineOrBytecode,
Bytes&& bytesToDelete,
const uint8_t* begin,
const uint8_t* end,
Uint32Vector&& callSiteLineNums)
: bytesToDelete(Move(bytesToDelete)),
begin(begin),
end(end),
index(index),
lineOrBytecode(lineOrBytecode),
callSiteLineNums(Move(callSiteLineNums))
{}
const uint8_t* begin() const { return begin_; }
const uint8_t* end() const { return end_; }
uint32_t index() const { return index_; }
uint32_t lineOrBytecode() const { return lineOrBytecode_; }
const Uint32Vector& callSiteLineNums() const { return callSiteLineNums_; }
FuncOffsets offsets() const { MOZ_ASSERT(finished_); return offsets_; }
void finish(FuncOffsets offsets) {
MOZ_ASSERT(!finished_);
offsets_ = offsets;
finished_ = true;
}
};
typedef Vector<FuncCompileUnit, 8, SystemAllocPolicy> FuncCompileUnitVector;
typedef Vector<FuncCompileInput, 8, SystemAllocPolicy> FuncCompileInputVector;
// CompiledCode contains the resulting code and metadata for a set of compiled
// input functions or stubs.
struct CompiledCode
{
Bytes bytes;
CodeRangeVector codeRanges;
CallSiteVector callSites;
CallSiteTargetVector callSiteTargets;
TrapSiteVector trapSites;
TrapFarJumpVector trapFarJumps;
CallFarJumpVector callFarJumps;
MemoryAccessVector memoryAccesses;
SymbolicAccessVector symbolicAccesses;
jit::CodeLabelVector codeLabels;
MOZ_MUST_USE bool swap(jit::MacroAssembler& masm);
void clear() {
bytes.clear();
codeRanges.clear();
callSites.clear();
callSiteTargets.clear();
trapSites.clear();
trapFarJumps.clear();
callFarJumps.clear();
memoryAccesses.clear();
symbolicAccesses.clear();
codeLabels.clear();
MOZ_ASSERT(empty());
}
bool empty() {
return bytes.empty() &&
codeRanges.empty() &&
callSites.empty() &&
callSiteTargets.empty() &&
trapSites.empty() &&
trapFarJumps.empty() &&
callFarJumps.empty() &&
memoryAccesses.empty() &&
symbolicAccesses.empty() &&
codeLabels.empty();
}
};
// The CompileTaskState of a ModuleGenerator contains the mutable state shared
// between helper threads executing CompileTasks. Each CompileTask started on a
@ -95,71 +119,22 @@ struct CompileTaskState
typedef ExclusiveData<CompileTaskState> ExclusiveCompileTaskState;
// A CompileTask represents the task of compiling a batch of functions. It is
// filled with a certain number of function's bodies that are sent off to a
// compilation helper thread, which fills in the resulting code offsets, and
// finally sent back to the validation thread.
// A CompileTask holds a batch of input functions that are to be compiled on a
// helper thread as well as, eventually, the results of compilation.
class CompileTask
struct CompileTask
{
const ModuleEnvironment& env_;
ExclusiveCompileTaskState& state_;
LifoAlloc lifo_;
Maybe<jit::TempAllocator> alloc_;
Maybe<jit::MacroAssembler> masm_;
FuncCompileUnitVector units_;
const ModuleEnvironment& env;
ExclusiveCompileTaskState& state;
LifoAlloc lifo;
FuncCompileInputVector inputs;
CompiledCode output;
CompileTask(const CompileTask&) = delete;
CompileTask& operator=(const CompileTask&) = delete;
void init() {
alloc_.emplace(&lifo_);
masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_);
}
public:
CompileTask(const ModuleEnvironment& env, ExclusiveCompileTaskState& state, size_t defaultChunkSize)
: env_(env),
state_(state),
lifo_(defaultChunkSize)
{
init();
}
LifoAlloc& lifo() {
return lifo_;
}
jit::TempAllocator& alloc() {
return *alloc_;
}
const ModuleEnvironment& env() const {
return env_;
}
const ExclusiveCompileTaskState& state() const {
return state_;
}
jit::MacroAssembler& masm() {
return *masm_;
}
FuncCompileUnitVector& units() {
return units_;
}
Tier tier() const {
return env_.tier;
}
CompileMode mode() const {
return env_.mode;
}
bool debugEnabled() const {
return env_.debug == DebugEnabled::True;
}
bool reset() {
units_.clear();
masm_.reset();
alloc_.reset();
lifo_.releaseAll();
init();
return true;
}
: env(env),
state(state),
lifo(defaultChunkSize)
{}
};
// A ModuleGenerator encapsulates the creation of a wasm module. During the
@ -173,7 +148,8 @@ class MOZ_STACK_CLASS ModuleGenerator
typedef HashSet<uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> Uint32Set;
typedef Vector<CompileTask, 0, SystemAllocPolicy> CompileTaskVector;
typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
typedef EnumeratedArray<Trap, Trap::Limit, CallableOffsets> TrapExitOffsetArray;
typedef EnumeratedArray<Trap, Trap::Limit, uint32_t> Uint32TrapArray;
typedef Vector<jit::CodeOffset, 0, SystemAllocPolicy> CodeOffsetVector;
// Constant parameters
SharedCompileArgs const compileArgs_;
@ -191,6 +167,7 @@ class MOZ_STACK_CLASS ModuleGenerator
// Data scoped to the ModuleGenerator's lifetime
ExclusiveCompileTaskState taskState_;
uint32_t numFuncDefs_;
uint32_t numSigs_;
uint32_t numTables_;
LifoAlloc lifo_;
@ -198,10 +175,15 @@ class MOZ_STACK_CLASS ModuleGenerator
jit::TempAllocator masmAlloc_;
jit::MacroAssembler masm_;
Uint32Vector funcToCodeRange_;
Uint32TrapArray trapCodeOffsets_;
uint32_t debugTrapCodeOffset_;
TrapFarJumpVector trapFarJumps_;
CallFarJumpVector callFarJumps_;
CallSiteTargetVector callSiteTargets_;
Uint32Set exportedFuncs_;
uint32_t lastPatchedCallsite_;
uint32_t lastPatchedCallSite_;
uint32_t startOfUnpatchedCallsites_;
Uint32Vector debugTrapFarJumps_;
CodeOffsetVector debugTrapFarJumps_;
// Parallel compilation
bool parallel_;
@ -214,37 +196,35 @@ class MOZ_STACK_CLASS ModuleGenerator
// Assertions
DebugOnly<bool> startedFuncDefs_;
DebugOnly<bool> finishedFuncDefs_;
DebugOnly<uint32_t> numFinishedFuncDefs_;
bool funcIsCompiled(uint32_t funcIndex) const;
const CodeRange& funcCodeRange(uint32_t funcIndex) const;
uint32_t numFuncImports() const;
MOZ_MUST_USE bool patchCallSites();
MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits, const Offsets& debugTrapStub);
MOZ_MUST_USE bool finishTask(CompileTask* task);
MOZ_MUST_USE bool finishOutstandingTask();
MOZ_MUST_USE bool finishFuncExports();
MOZ_MUST_USE bool finishCodegen();
MOZ_MUST_USE bool finishLinkData();
void generateBytecodeHash(const ShareableBytes& bytecode);
MOZ_MUST_USE bool finishMetadata(const ShareableBytes& bytecode);
MOZ_MUST_USE UniqueConstCodeSegment finishCodeSegment(const ShareableBytes& bytecode);
bool linkCallSites();
void noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange);
bool linkCompiledCode(const CompiledCode& code);
bool finishTask(CompileTask* task);
bool finishOutstandingTask();
bool finishFuncExports();
bool finishLinking();
bool finishMetadata(const ShareableBytes& bytecode);
UniqueConstCodeSegment finishCodeSegment(const ShareableBytes& bytecode);
UniqueJumpTable createJumpTable(const CodeSegment& codeSegment);
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);
bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
bool allocateGlobal(GlobalDesc* global);
MOZ_MUST_USE bool launchBatchCompile();
MOZ_MUST_USE bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
Bytes&& bytes, const uint8_t* begin, const uint8_t* end,
Uint32Vector&& lineNums);
bool launchBatchCompile();
bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode,
Bytes&& bytes, const uint8_t* begin, const uint8_t* end,
Uint32Vector&& lineNums);
MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata);
MOZ_MUST_USE bool initWasm();
bool initAsmJS(Metadata* asmJSMetadata);
bool initWasm(size_t codeLength);
bool isAsmJS() const { return env_->isAsmJS(); }
Tier tier() const { return env_->tier; }
CompileMode mode() const { return env_->mode; }
Tier tier() const { return env_->tier(); }
CompileMode mode() const { return env_->mode(); }
bool debugEnabled() const { return env_->debugEnabled(); }
public:
@ -252,7 +232,7 @@ class MOZ_STACK_CLASS ModuleGenerator
Atomic<bool>* cancelled, UniqueChars* error);
~ModuleGenerator();
MOZ_MUST_USE bool init(Metadata* maybeAsmJSMetadata = nullptr);
MOZ_MUST_USE bool init(size_t codeSectionSize, Metadata* maybeAsmJSMetadata = nullptr);
// Function definitions:
MOZ_MUST_USE bool startFuncDefs();

View File

@ -126,7 +126,7 @@ class FunctionCompiler
const ModuleEnvironment& env_;
IonOpIter iter_;
const FuncCompileUnit& func_;
const FuncCompileInput& func_;
const ValTypeVector& locals_;
size_t lastReadCallSite_;
@ -149,7 +149,7 @@ class FunctionCompiler
public:
FunctionCompiler(const ModuleEnvironment& env,
Decoder& decoder,
const FuncCompileUnit& func,
const FuncCompileInput& func,
const ValTypeVector& locals,
MIRGenerator& mirGen)
: env_(env),
@ -171,7 +171,7 @@ class FunctionCompiler
const ModuleEnvironment& env() const { return env_; }
IonOpIter& iter() { return iter_; }
TempAllocator& alloc() const { return alloc_; }
const Sig& sig() const { return *env_.funcSigs[func_.index()]; }
const Sig& sig() const { return *env_.funcSigs[func_.index]; }
BytecodeOffset bytecodeOffset() const {
return iter_.bytecodeOffset();
@ -270,7 +270,7 @@ class FunctionCompiler
#endif
MOZ_ASSERT(inDeadCode());
MOZ_ASSERT(done(), "all bytes must be consumed");
MOZ_ASSERT(func_.callSiteLineNums().length() == lastReadCallSite_);
MOZ_ASSERT(func_.callSiteLineNums.length() == lastReadCallSite_);
}
/************************* Read-only interface (after local scope setup) */
@ -1574,8 +1574,8 @@ class FunctionCompiler
/************************************************************ DECODING ***/
uint32_t readCallSiteLineOrBytecode() {
if (!func_.callSiteLineNums().empty())
return func_.callSiteLineNums()[lastReadCallSite_++];
if (!func_.callSiteLineNums.empty())
return func_.callSiteLineNums[lastReadCallSite_++];
return iter_.lastOpcodeOffset();
}
@ -3852,70 +3852,86 @@ EmitBodyExprs(FunctionCompiler& f)
}
bool
wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* func, UniqueChars* error)
wasm::IonCompileFunctions(const ModuleEnvironment& env, LifoAlloc& lifo,
const FuncCompileInputVector& inputs, CompiledCode* code,
UniqueChars* error)
{
MOZ_ASSERT(task->tier() == Tier::Ion);
MOZ_ASSERT(env.tier() == Tier::Ion);
const ModuleEnvironment& env = task->env();
TempAllocator alloc(&lifo);
JitContext jitContext(&alloc);
MOZ_ASSERT(IsCompilingWasm());
MacroAssembler masm(MacroAssembler::WasmToken(), alloc);
Decoder d(func->begin(), func->end(), func->lineOrBytecode(), error);
// Build the local types vector.
ValTypeVector locals;
if (!locals.appendAll(task->env().funcSigs[func->index()]->args()))
return false;
if (!DecodeLocalEntries(d, env.kind, &locals))
// Swap in already-allocated empty vectors to avoid malloc/free.
MOZ_ASSERT(code->empty());
if (!code->swap(masm))
return false;
// Set up for Ion compilation.
for (const FuncCompileInput& func : inputs) {
Decoder d(func.begin, func.end, func.lineOrBytecode, error);
JitContext jitContext(&task->alloc());
const JitCompileOptions options;
MIRGraph graph(&task->alloc());
CompileInfo compileInfo(locals.length());
MIRGenerator mir(nullptr, options, &task->alloc(), &graph, &compileInfo,
IonOptimizations.get(OptimizationLevel::Wasm));
mir.initMinWasmHeapLength(env.minMemoryLength);
// Build the local types vector.
// Build MIR graph
{
FunctionCompiler f(env, d, *func, locals, mir);
if (!f.init())
ValTypeVector locals;
if (!locals.appendAll(env.funcSigs[func.index]->args()))
return false;
if (!DecodeLocalEntries(d, env.kind, &locals))
return false;
if (!f.startBlock())
return false;
// Set up for Ion compilation.
if (!EmitBodyExprs(f))
return false;
const JitCompileOptions options;
MIRGraph graph(&alloc);
CompileInfo compileInfo(locals.length());
MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo,
IonOptimizations.get(OptimizationLevel::Wasm));
mir.initMinWasmHeapLength(env.minMemoryLength);
f.finish();
// Build MIR graph
{
FunctionCompiler f(env, d, func, locals, mir);
if (!f.init())
return false;
if (!f.startBlock())
return false;
if (!EmitBodyExprs(f))
return false;
f.finish();
}
// Compile MIR graph
{
jit::SpewBeginFunction(&mir, nullptr);
jit::AutoSpewEndFunction spewEndFunction(&mir);
if (!OptimizeMIR(&mir))
return false;
LIRGraph* lir = GenerateLIR(&mir);
if (!lir)
return false;
SigIdDesc sigId = env.funcSigs[func.index]->id;
CodeGenerator codegen(&mir, lir, &masm);
BytecodeOffset prologueTrapOffset(func.lineOrBytecode);
FuncOffsets offsets;
if (!codegen.generateWasm(sigId, prologueTrapOffset, &offsets))
return false;
if (!code->codeRanges.emplaceBack(func.index, func.lineOrBytecode, offsets))
return false;
}
}
// Compile MIR graph
{
jit::SpewBeginFunction(&mir, nullptr);
jit::AutoSpewEndFunction spewEndFunction(&mir);
masm.finish();
if (masm.oom())
return false;
if (!OptimizeMIR(&mir))
return false;
LIRGraph* lir = GenerateLIR(&mir);
if (!lir)
return false;
SigIdDesc sigId = env.funcSigs[func->index()]->id;
CodeGenerator codegen(&mir, lir, &task->masm());
BytecodeOffset prologueTrapOffset(func->lineOrBytecode());
FuncOffsets offsets;
if (!codegen.generateWasm(sigId, prologueTrapOffset, &offsets))
return false;
func->finish(offsets);
}
return true;
return code->swap(masm);
}

View File

@ -21,17 +21,16 @@
#include "mozilla/Attributes.h"
#include "wasm/WasmTypes.h"
#include "wasm/WasmGenerator.h"
namespace js {
namespace wasm {
class CompileTask;
class FuncCompileUnit;
// Generates very fast code at the expense of compilation time.
MOZ_MUST_USE bool
IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error);
IonCompileFunctions(const ModuleEnvironment& env, LifoAlloc& lifo,
const FuncCompileInputVector& inputs, CompiledCode* code,
UniqueChars* error);
} // namespace wasm
} // namespace js

View File

@ -41,34 +41,6 @@ using namespace js::wasm;
using mozilla::IsNaN;
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
// On MIPS, CodeLabels are instruction immediates so InternalLinks only
// patch instruction immediates.
LinkDataTier::InternalLink::InternalLink(Kind kind)
{
MOZ_ASSERT(kind == CodeLabel || kind == InstructionImmediate);
}
bool
LinkDataTier::InternalLink::isRawPointerPatch()
{
return false;
}
#else
// On the rest, CodeLabels are raw pointers so InternalLinks only patch
// raw pointers.
LinkDataTier::InternalLink::InternalLink(Kind kind)
{
MOZ_ASSERT(kind == CodeLabel || kind == RawPointer);
}
bool
LinkDataTier::InternalLink::isRawPointerPatch()
{
return true;
}
#endif
size_t
LinkDataTier::SymbolicLinkArray::serializedSize() const
{

View File

@ -35,12 +35,13 @@ struct CompileArgs;
// LinkData contains all the metadata necessary to patch all the locations
// that depend on the absolute address of a CodeSegment.
//
// LinkData is built incrementing by ModuleGenerator and then stored immutably
// in Module.
// LinkData is built incrementally by ModuleGenerator and then stored immutably
// in Module. LinkData is distinct from Metadata in that LinkData is owned and
// destroyed by the Module since it is not needed after instantiation; Metadata
// is needed at runtime.
struct LinkDataTierCacheablePod
{
uint32_t functionCodeLength;
uint32_t interruptOffset;
uint32_t outOfBoundsOffset;
uint32_t unalignedAccessOffset;
@ -58,17 +59,8 @@ struct LinkDataTier : LinkDataTierCacheablePod
const LinkDataTierCacheablePod& pod() const { return *this; }
struct InternalLink {
enum Kind {
RawPointer,
CodeLabel,
InstructionImmediate
};
MOZ_INIT_OUTSIDE_CTOR uint32_t patchAtOffset;
MOZ_INIT_OUTSIDE_CTOR uint32_t targetOffset;
InternalLink() = default;
explicit InternalLink(Kind kind);
bool isRawPointerPatch();
uint32_t patchAtOffset;
uint32_t targetOffset;
};
typedef Vector<InternalLink, 0, SystemAllocPolicy> InternalLinkVector;

28
js/src/wasm/WasmSignalHandlers.cpp Executable file → Normal file
View File

@ -783,7 +783,7 @@ MOZ_COLD static void
HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
const Instance& instance, WasmActivation* activation, uint8_t** ppc)
{
MOZ_RELEASE_ASSERT(instance.code().containsFunctionPC(pc));
MOZ_RELEASE_ASSERT(instance.code().containsCodePC(pc));
const CodeSegment* segment;
const MemoryAccess* memoryAccess = instance.code().lookupMemoryAccess(pc, &segment);
@ -819,7 +819,7 @@ HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddr
uint8_t* end = Disassembler::DisassembleHeapAccess(pc, &access);
const Disassembler::ComplexAddress& address = access.address();
MOZ_RELEASE_ASSERT(end > pc);
MOZ_RELEASE_ASSERT(segment->containsFunctionPC(end));
MOZ_RELEASE_ASSERT(segment->containsCodePC(end));
// Check x64 asm.js heap access invariants.
MOZ_RELEASE_ASSERT(address.disp() >= 0);
@ -932,7 +932,7 @@ MOZ_COLD static void
HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
const Instance& instance, WasmActivation* activation, uint8_t** ppc)
{
MOZ_RELEASE_ASSERT(instance.code().containsFunctionPC(pc));
MOZ_RELEASE_ASSERT(instance.code().containsCodePC(pc));
const CodeSegment* segment;
const MemoryAccess* memoryAccess = instance.code().lookupMemoryAccess(pc, &segment);
@ -999,7 +999,8 @@ HandleFault(PEXCEPTION_POINTERS exception)
if (!code)
return false;
if (!codeSegment->containsFunctionPC(pc)) {
const Instance* instance = LookupFaultingInstance(*code, pc, ContextToFP(context));
if (!instance) {
// On Windows, it is possible for InterruptRunningJitCode to execute
// between a faulting heap access and the handling of the fault due
// to InterruptRunningJitCode's use of SuspendThread. When this happens,
@ -1013,7 +1014,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
for (auto t : code->tiers()) {
if (pc == code->segment(t).interruptCode() &&
activation->interrupted() &&
code->segment(t).containsFunctionPC(activation->resumePC()))
code->segment(t).containsCodePC(activation->resumePC()))
{
return true;
}
@ -1021,10 +1022,6 @@ HandleFault(PEXCEPTION_POINTERS exception)
return false;
}
const Instance* instance = LookupFaultingInstance(*code, pc, ContextToFP(context));
if (!instance)
return false;
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
// This check isn't necessary, but, since we can, check anyway to make
@ -1145,7 +1142,7 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
return false;
const Instance* instance = LookupFaultingInstance(*code, pc, ContextToFP(&context));
if (!instance || !instance->code().containsFunctionPC(pc))
if (!instance)
return false;
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
@ -1357,7 +1354,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
return false;
const Instance* instance = LookupFaultingInstance(*code, pc, ContextToFP(context));
if (!instance || !instance->code().containsFunctionPC(pc, &segment))
if (!instance)
return false;
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
@ -1457,11 +1454,16 @@ wasm::InInterruptibleCode(JSContext* cx, uint8_t* pc, const CodeSegment** cs)
// Only interrupt in function code so that the frame iterators have the
// invariant that resumePC always has a function CodeRange and we can't
// get into any weird interrupt-during-interrupt-stub cases.
if (!cx->compartment())
return false;
const Code* code = cx->compartment()->wasm.lookupCode(pc, cs);
return code && (*cs)->containsFunctionPC(pc);
const Code* code = cx->compartment()->wasm.lookupCode(pc);
if (!code)
return false;
const CodeRange* codeRange = code->lookupRange(pc, cs);
return codeRange && codeRange->isFunction();
}
// The return value indicates whether the PC was changed, not whether there was

View File

@ -19,6 +19,7 @@
#include "wasm/WasmStubs.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/EnumeratedRange.h"
#include "wasm/WasmCode.h"
#include "wasm/WasmGenerator.h"
@ -30,18 +31,20 @@ using namespace js::jit;
using namespace js::wasm;
using mozilla::ArrayLength;
using mozilla::MakeEnumeratedRange;
typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
static void
static bool
FinishOffsets(MacroAssembler& masm, Offsets* offsets)
{
// On old ARM hardware, constant pools could be inserted and they need to
// be flushed before considering the size of the masm.
masm.flushBuffer();
offsets->end = masm.size();
return !masm.oom();
}
static void
@ -252,13 +255,12 @@ static const unsigned FailFP = 0xbad;
// signature of the entry point is Module::ExportFuncPtr. The exported wasm
// function has an ABI derived from its specific signature, so this function
// must map from the ABI of ExportFuncPtr to the export's signature's ABI.
Offsets
wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe)
static bool
GenerateEntry(MacroAssembler& masm, const FuncExport& fe, Offsets* offsets)
{
masm.haltingAlign(CodeAlignment);
Offsets offsets;
offsets.begin = masm.currentOffset();
offsets->begin = masm.currentOffset();
// Save the return address if it wasn't already saved by the call insn.
#if defined(JS_CODEGEN_ARM)
@ -371,8 +373,7 @@ wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe)
masm.ret();
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
static void
@ -500,15 +501,15 @@ FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argO
// - a table entry, so JS imports can be put into tables
// - normal entries, so that, if the import is re-exported, an entry stub can
// be generated and called without any special cases
FuncOffsets
wasm::GenerateImportFunction(jit::MacroAssembler& masm, const FuncImport& fi, SigIdDesc sigId)
static bool
GenerateImportFunction(jit::MacroAssembler& masm, const FuncImport& fi, SigIdDesc sigId,
FuncOffsets* offsets)
{
masm.setFramePushed(0);
unsigned framePushed = StackDecrementForCall(masm, WasmStackAlignment, fi.sig().args());
FuncOffsets offsets;
GenerateFunctionPrologue(masm, framePushed, sigId, &offsets);
GenerateFunctionPrologue(masm, framePushed, sigId, offsets);
// The argument register state is already setup by our caller. We just need
// to be sure not to clobber it before the call.
@ -534,20 +535,19 @@ wasm::GenerateImportFunction(jit::MacroAssembler& masm, const FuncImport& fi, Si
masm.loadWasmTlsRegFromFrame();
masm.loadWasmPinnedRegsFromTls();
GenerateFunctionEpilogue(masm, framePushed, &offsets);
GenerateFunctionEpilogue(masm, framePushed, offsets);
masm.wasmEmitTrapOutOfLineCode();
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
// Generate a stub that is called via the internal ABI derived from the
// signature of the import and calls into an appropriate callImport C++
// function, having boxed all the ABI arguments into a homogeneous Value array.
CallableOffsets
wasm::GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex,
Label* throwLabel)
static bool
GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex,
Label* throwLabel, CallableOffsets* offsets)
{
masm.setFramePushed(0);
@ -567,8 +567,7 @@ wasm::GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint3
unsigned argBytes = Max<size_t>(1, fi.sig().args().length()) * sizeof(Value);
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, argOffset + argBytes);
CallableOffsets offsets;
GenerateExitPrologue(masm, framePushed, ExitReason::Fixed::ImportInterp, &offsets);
GenerateExitPrologue(masm, framePushed, ExitReason::Fixed::ImportInterp, offsets);
// Fill the argument array.
unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
@ -663,17 +662,17 @@ wasm::GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint3
MOZ_ASSERT(NonVolatileRegs.has(HeapReg));
#endif
GenerateExitEpilogue(masm, framePushed, ExitReason::Fixed::ImportInterp, &offsets);
GenerateExitEpilogue(masm, framePushed, ExitReason::Fixed::ImportInterp, offsets);
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
// Generate a stub that is called via the internal ABI derived from the
// signature of the import and calls into a compatible JIT function,
// having boxed all the ABI arguments into the JIT stack frame layout.
CallableOffsets
wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLabel)
static bool
GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLabel,
CallableOffsets* offsets)
{
masm.setFramePushed(0);
@ -690,8 +689,7 @@ wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* t
unsigned jitFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalJitFrameBytes) -
sizeOfRetAddr;
CallableOffsets offsets;
GenerateExitPrologue(masm, jitFramePushed, ExitReason::Fixed::ImportJit, &offsets);
GenerateExitPrologue(masm, jitFramePushed, ExitReason::Fixed::ImportJit, offsets);
// 1. Descriptor
size_t argOffset = 0;
@ -838,7 +836,7 @@ wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* t
Label done;
masm.bind(&done);
GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::Fixed::ImportJit, &offsets);
GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::Fixed::ImportJit, offsets);
if (oolConvert.used()) {
masm.bind(&oolConvert);
@ -896,8 +894,7 @@ wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* t
MOZ_ASSERT(masm.framePushed() == 0);
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
struct ABIFunctionArgs
@ -927,17 +924,16 @@ struct ABIFunctionArgs
}
};
CallableOffsets
bool
wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType, ExitReason exitReason,
void* funcPtr)
void* funcPtr, CallableOffsets* offsets)
{
masm.setFramePushed(0);
ABIFunctionArgs args(abiType);
uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
CallableOffsets offsets;
GenerateExitPrologue(masm, framePushed, exitReason, &offsets);
GenerateExitPrologue(masm, framePushed, exitReason, offsets);
// Copy out and convert caller arguments, if needed.
unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
@ -985,17 +981,16 @@ wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType, ExitRe
masm.ma_vxfer(r0, r1, d0);
#endif
GenerateExitEpilogue(masm, framePushed, exitReason, &offsets);
offsets.end = masm.currentOffset();
return offsets;
GenerateExitEpilogue(masm, framePushed, exitReason, offsets);
return FinishOffsets(masm, offsets);
}
// Generate a stub that calls into ReportTrap with the right trap reason.
// This stub is called with ABIStackAlignment by a trap out-of-line path. An
// exit prologue/epilogue is used so that stack unwinding picks up the
// current WasmActivation. Unwinding will begin at the caller of this trap exit.
CallableOffsets
wasm::GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel)
static bool
GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel, CallableOffsets* offsets)
{
masm.haltingAlign(CodeAlignment);
@ -1006,8 +1001,7 @@ wasm::GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel)
uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
CallableOffsets offsets;
GenerateExitPrologue(masm, framePushed, ExitReason::Fixed::Trap, &offsets);
GenerateExitPrologue(masm, framePushed, ExitReason::Fixed::Trap, offsets);
ABIArgMIRTypeIter i(args);
if (i->kind() == ABIArg::GPR)
@ -1022,10 +1016,9 @@ wasm::GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel)
masm.jump(throwLabel);
GenerateExitEpilogue(masm, framePushed, ExitReason::Fixed::Trap, &offsets);
GenerateExitEpilogue(masm, framePushed, ExitReason::Fixed::Trap, offsets);
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
// Generate a stub which is only used by the signal handlers to handle out of
@ -1036,13 +1029,13 @@ wasm::GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel)
// be lost. This stub should be removed when SIMD.js and Atomics are moved to
// wasm and given proper traps and when we use a non-faulting strategy for
// unaligned ARM access.
static Offsets
GenerateGenericMemoryAccessTrap(MacroAssembler& masm, SymbolicAddress reporter, Label* throwLabel)
static bool
GenerateGenericMemoryAccessTrap(MacroAssembler& masm, SymbolicAddress reporter, Label* throwLabel,
Offsets* offsets)
{
masm.haltingAlign(CodeAlignment);
Offsets offsets;
offsets.begin = masm.currentOffset();
offsets->begin = masm.currentOffset();
// sp can be anything at this point, so ensure it is aligned when calling
// into C++. We unconditionally jump to throw so don't worry about
@ -1054,20 +1047,21 @@ GenerateGenericMemoryAccessTrap(MacroAssembler& masm, SymbolicAddress reporter,
masm.call(reporter);
masm.jump(throwLabel);
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
Offsets
wasm::GenerateOutOfBoundsExit(MacroAssembler& masm, Label* throwLabel)
static bool
GenerateOutOfBoundsExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
{
return GenerateGenericMemoryAccessTrap(masm, SymbolicAddress::ReportOutOfBounds, throwLabel);
return GenerateGenericMemoryAccessTrap(masm, SymbolicAddress::ReportOutOfBounds, throwLabel,
offsets);
}
Offsets
wasm::GenerateUnalignedExit(MacroAssembler& masm, Label* throwLabel)
static bool
GenerateUnalignedExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
{
return GenerateGenericMemoryAccessTrap(masm, SymbolicAddress::ReportUnalignedAccess, throwLabel);
return GenerateGenericMemoryAccessTrap(masm, SymbolicAddress::ReportUnalignedAccess, throwLabel,
offsets);
}
#if defined(JS_CODEGEN_ARM)
@ -1089,13 +1083,12 @@ static const LiveRegisterSet AllRegsExceptSP(
// preserve *all* register state. If execution is interrupted, the entire
// activation will be popped by the throw stub, so register state does not need
// to be restored.
Offsets
wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
static bool
GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
{
masm.haltingAlign(CodeAlignment);
Offsets offsets;
offsets.begin = masm.currentOffset();
offsets->begin = masm.currentOffset();
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
// Be very careful here not to perturb the machine state before saving it
@ -1236,8 +1229,7 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
# error "Unknown architecture!"
#endif
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
// Generate a stub that restores the stack pointer to what it was on entry to
@ -1245,15 +1237,14 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
// return which will return from this wasm activation to the caller. This stub
// should only be called after the caller has reported an error (or, in the case
// of the interrupt stub, intends to interrupt execution).
Offsets
wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
static bool
GenerateThrowStub(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
{
masm.haltingAlign(CodeAlignment);
masm.bind(throwLabel);
Offsets offsets;
offsets.begin = masm.currentOffset();
offsets->begin = masm.currentOffset();
// The throw stub can be jumped to from an async interrupt that is halting
// execution. Thus the stack pointer can be unaligned and we must align it
@ -1270,8 +1261,7 @@ wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
masm.move32(Imm32(FailFP), FramePointer);
masm.ret();
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
static const LiveRegisterSet AllAllocatableRegs = LiveRegisterSet(
@ -1281,15 +1271,14 @@ static const LiveRegisterSet AllAllocatableRegs = LiveRegisterSet(
// Generate a stub that handle toggable enter/leave frame traps or breakpoints.
// The trap records frame pointer (via GenerateExitPrologue) and saves most of
// registers to not affect the code generated by WasmBaselineCompile.
Offsets
wasm::GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel)
static bool
GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel, CallableOffsets* offsets)
{
masm.haltingAlign(CodeAlignment);
masm.setFramePushed(0);
CallableOffsets offsets;
GenerateExitPrologue(masm, 0, ExitReason::Fixed::DebugTrap, &offsets);
GenerateExitPrologue(masm, 0, ExitReason::Fixed::DebugTrap, offsets);
// Save all registers used between baseline compiler operations.
masm.PushRegsInMask(AllAllocatableRegs);
@ -1319,8 +1308,100 @@ wasm::GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel)
masm.setFramePushed(framePushed);
masm.PopRegsInMask(AllAllocatableRegs);
GenerateExitEpilogue(masm, 0, ExitReason::Fixed::DebugTrap, &offsets);
GenerateExitEpilogue(masm, 0, ExitReason::Fixed::DebugTrap, offsets);
FinishOffsets(masm, &offsets);
return offsets;
return FinishOffsets(masm, offsets);
}
static const unsigned STUBS_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
bool
wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& imports,
const FuncExportVector& exports, CompiledCode* code)
{
LifoAlloc lifo(STUBS_LIFO_DEFAULT_CHUNK_SIZE);
TempAllocator alloc(&lifo);
MacroAssembler masm(MacroAssembler::WasmToken(), alloc);
// Swap in already-allocated empty vectors to avoid malloc/free.
if (!code->swap(masm))
return false;
Label throwLabel;
for (uint32_t funcIndex = 0; funcIndex < imports.length(); funcIndex++) {
const FuncImport& fi = imports[funcIndex];
CallableOffsets offsets;
if (!GenerateImportInterpExit(masm, fi, funcIndex, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::ImportInterpExit, funcIndex, offsets))
return false;
if (!GenerateImportJitExit(masm, fi, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::ImportJitExit, funcIndex, offsets))
return false;
if (!env.isAsmJS()) {
FuncOffsets offsets;
if (!GenerateImportFunction(masm, fi, env.funcSigs[funcIndex]->id, &offsets))
return false;
if (!code->codeRanges.emplaceBack(funcIndex, /* bytecodeOffset = */ 0, offsets))
return false;
}
}
for (const FuncExport& fe : exports) {
Offsets offsets;
if (!GenerateEntry(masm, fe, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::Entry, fe.funcIndex(), offsets))
return false;
}
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
CallableOffsets offsets;
if (!GenerateTrapExit(masm, trap, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(trap, offsets))
return false;
}
Offsets offsets;
if (!GenerateOutOfBoundsExit(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::OutOfBoundsExit, offsets))
return false;
if (!GenerateUnalignedExit(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::UnalignedExit, offsets))
return false;
if (!GenerateInterruptExit(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::Interrupt, offsets))
return false;
{
CallableOffsets offsets;
if (!GenerateDebugTrapStub(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::DebugTrap, offsets))
return false;
}
if (!GenerateThrowStub(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::Throw, offsets))
return false;
masm.finish();
if (masm.oom())
return false;
return code->swap(masm);
}

Some files were not shown because too many files have changed in this diff Show More