mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Merge m-c to autoland, a=merge
MozReview-Commit-ID: BelD7GUGNq6
This commit is contained in:
commit
528cd7a92c
@ -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
|
||||
|
@ -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(); };
|
||||
}));
|
||||
}
|
||||
|
@ -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(); };
|
||||
}));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -23,9 +23,7 @@ enum XMLHttpRequestResponseType {
|
||||
"text",
|
||||
|
||||
// Mozilla-specific stuff
|
||||
"moz-chunked-text",
|
||||
"moz-chunked-arraybuffer",
|
||||
"moz-blob"
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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)();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
|
@ -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__
|
||||
|
@ -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);
|
||||
}
|
@ -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;;;;;;;;;;;;
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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.
|
@ -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
@ -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 */
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -332,7 +332,6 @@ CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph, MacroAssembler*
|
||||
|
||||
CodeGenerator::~CodeGenerator()
|
||||
{
|
||||
MOZ_ASSERT_IF(!gen->compilingWasm(), masm.numSymbolicAccesses() == 0);
|
||||
js_delete(scriptCounts_);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -3136,7 +3136,7 @@ MacroAssembler::wasmEmitTrapOutOfLineCode()
|
||||
// iterator to find the right CodeRange while walking the stack.
|
||||
breakpoint();
|
||||
|
||||
clearTrapSites();
|
||||
trapSites().clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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(); }
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -56,7 +56,7 @@ enum class ParseTaskKind
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class CompileTask;
|
||||
struct CompileTask;
|
||||
typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
|
||||
|
||||
struct Tier2GeneratorTask
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_; }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
28
js/src/wasm/WasmSignalHandlers.cpp
Executable file → Normal 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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user