mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-18 06:45:33 +00:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
9aa5f90ca7
@ -525,11 +525,8 @@ Accessible::ChildAtPoint(int32_t aX, int32_t aY,
|
||||
{
|
||||
// If we can't find the point in a child, we will return the fallback answer:
|
||||
// we return |this| if the point is within it, otherwise nullptr.
|
||||
nsIntRect rect = Bounds();
|
||||
if (rect.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
Accessible* fallbackAnswer = nullptr;
|
||||
nsIntRect rect = Bounds();
|
||||
if (aX >= rect.x && aX < rect.x + rect.width &&
|
||||
aY >= rect.y && aY < rect.y + rect.height)
|
||||
fallbackAnswer = this;
|
||||
|
@ -68,6 +68,8 @@ if (!MAC) {
|
||||
hitTest("imgmap", theLetterA, theLetterA);
|
||||
hitTest("container", "imgmap", theLetterA);
|
||||
|
||||
// hit testing for element contained by zero-width element
|
||||
hitTest("container2", "container2_input", "container2_input");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -106,5 +108,8 @@ if (!MAC) {
|
||||
<img id="imgmap" width="447" height="15" usemap="#atoz_map" src="../letters.gif"/>
|
||||
</div>
|
||||
|
||||
<div id="container2" style="width: 0px">
|
||||
<input id="container2_input">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1186,7 +1186,7 @@ pref("security.sandbox.windows.log", false);
|
||||
// To get a different setting for a particular plugin replace "default", with
|
||||
// the plugin's nice file name, see: nsPluginTag::GetNiceFileName.
|
||||
pref("dom.ipc.plugins.sandbox.default", false);
|
||||
pref("dom.ipc.plugins.sandbox.flash", false);
|
||||
pref("dom.ipc.plugins.sandbox.flash", true);
|
||||
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
// This controls whether the Windows content process sandbox is using a more
|
||||
|
@ -3383,10 +3383,8 @@ nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
|
||||
{
|
||||
SetIsActive(value);
|
||||
}
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetIsPrerendered(&value))) {
|
||||
if (value) {
|
||||
SetIsPrerendered(true);
|
||||
}
|
||||
if (parentAsDocShell->GetIsPrerendered()) {
|
||||
SetIsPrerendered(true);
|
||||
}
|
||||
if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
|
||||
value = false;
|
||||
|
@ -624,7 +624,7 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
* native code to be able to put a docshell in prerendering.
|
||||
*/
|
||||
[noscript] void SetIsPrerendered(in boolean prerendered);
|
||||
readonly attribute boolean isPrerendered;
|
||||
[infallible] readonly attribute boolean isPrerendered;
|
||||
|
||||
/**
|
||||
* The ID of the docshell in the session history.
|
||||
|
@ -397,10 +397,10 @@ EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
|
||||
lm->~EventListenerManagerMapEntry();
|
||||
}
|
||||
|
||||
class SameOriginChecker MOZ_FINAL : public nsIChannelEventSink,
|
||||
public nsIInterfaceRequestor
|
||||
class SameOriginCheckerImpl MOZ_FINAL : public nsIChannelEventSink,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
~SameOriginChecker() {}
|
||||
~SameOriginCheckerImpl() {}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
@ -5648,11 +5648,11 @@ nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr)
|
||||
|
||||
/* static */
|
||||
nsIInterfaceRequestor*
|
||||
nsContentUtils::GetSameOriginChecker()
|
||||
nsContentUtils::SameOriginChecker()
|
||||
{
|
||||
if (!sSameOriginChecker) {
|
||||
sSameOriginChecker = new SameOriginChecker();
|
||||
NS_IF_ADDREF(sSameOriginChecker);
|
||||
sSameOriginChecker = new SameOriginCheckerImpl();
|
||||
NS_ADDREF(sSameOriginChecker);
|
||||
}
|
||||
return sSameOriginChecker;
|
||||
}
|
||||
@ -5683,15 +5683,15 @@ nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(SameOriginChecker,
|
||||
NS_IMPL_ISUPPORTS(SameOriginCheckerImpl,
|
||||
nsIChannelEventSink,
|
||||
nsIInterfaceRequestor)
|
||||
|
||||
NS_IMETHODIMP
|
||||
SameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *cb)
|
||||
SameOriginCheckerImpl::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback* cb)
|
||||
{
|
||||
NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
|
||||
|
||||
@ -5704,7 +5704,7 @@ SameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
|
||||
SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
@ -1635,7 +1635,7 @@ public:
|
||||
|
||||
// Returns NS_OK for same origin, error (NS_ERROR_DOM_BAD_URI) if not.
|
||||
static nsresult CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel);
|
||||
static nsIInterfaceRequestor* GetSameOriginChecker();
|
||||
static nsIInterfaceRequestor* SameOriginChecker();
|
||||
|
||||
/**
|
||||
* Get the Origin of the passed in nsIPrincipal or nsIURI. If the passed in
|
||||
|
@ -1337,8 +1337,7 @@ nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
|
||||
|
||||
nsIDocument* doc = aRequestingNode->OwnerDoc();
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
|
||||
NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::SameOriginChecker();
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
|
@ -4342,10 +4342,11 @@ nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj)
|
||||
nsGlobalWindow::IsPrivilegedChromeWindow(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
// For now, have to deal with XPConnect objects here.
|
||||
return xpc::WindowOrNull(aObj)->IsChromeWindow();
|
||||
return xpc::WindowOrNull(aObj)->IsChromeWindow() &&
|
||||
nsContentUtils::ObjectPrincipal(aObj) == nsContentUtils::GetSystemPrincipal();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -491,7 +491,7 @@ public:
|
||||
|
||||
void GetSupportedNames(nsTArray<nsString>& aNames);
|
||||
|
||||
static bool IsChromeWindow(JSContext* /* unused */, JSObject* aObj);
|
||||
static bool IsPrivilegedChromeWindow(JSContext* /* unused */, JSObject* aObj);
|
||||
|
||||
static bool IsShowModalDialogEnabled(JSContext* /* unused */ = nullptr,
|
||||
JSObject* /* unused */ = nullptr);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDOMGlobalPropertyInitializer.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPrincipal.h"
|
||||
@ -2381,12 +2382,47 @@ CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CheckSafetyInPrerendering(JSContext* aCx, JSObject* aObj)
|
||||
void
|
||||
HandlePrerenderingViolation(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
//TODO: Check if page is being prerendered.
|
||||
//Returning false for now.
|
||||
return false;
|
||||
// Suspend the window and its workers, and its children too.
|
||||
aWindow->SuspendTimeouts();
|
||||
|
||||
// Suspend event handling on the document
|
||||
nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
|
||||
if (doc) {
|
||||
doc->SuppressEventHandling(nsIDocument::eEvents);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
JS::Rooted<JSObject*> thisObj(aCx, js::CheckedUnwrap(aObj));
|
||||
if (!thisObj) {
|
||||
// Without a this object, we cannot check the safety.
|
||||
return true;
|
||||
}
|
||||
nsGlobalWindow* window = xpc::WindowGlobalOrNull(thisObj);
|
||||
if (!window) {
|
||||
// Without a window, we cannot check the safety.
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIDocShell* docShell = window->GetDocShell();
|
||||
if (!docShell) {
|
||||
// Without a docshell, we cannot check the safety.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (docShell->GetIsPrerendered()) {
|
||||
HandlePrerenderingViolation(window);
|
||||
// When the bindings layer sees a false return value, it returns false form
|
||||
// the JSNative in order to trigger an uncatchable exception.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -3142,9 +3142,20 @@ AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
|
||||
bool
|
||||
CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
|
||||
|
||||
//Returns true if page is being prerendered.
|
||||
// This function is called by the bindings layer for methods/getters/setters
|
||||
// that are not safe to be called in prerendering mode. It checks to make sure
|
||||
// that the |this| object is not running in a global that is in prerendering
|
||||
// mode. Otherwise, it aborts execution of timers and event handlers, and
|
||||
// returns false which gets converted to an uncatchable exception by the
|
||||
// bindings layer.
|
||||
bool
|
||||
CheckSafetyInPrerendering(JSContext* aCx, JSObject* aObj);
|
||||
EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
// Handles the violation of a blacklisted action in prerendering mode by
|
||||
// aborting the scripts, and preventing timers and event handlers from running
|
||||
// in the window in the future.
|
||||
void
|
||||
HandlePrerenderingViolation(nsPIDOMWindow* aWindow);
|
||||
|
||||
bool
|
||||
CallerSubsumes(JSObject* aObject);
|
||||
|
@ -1829,7 +1829,7 @@ addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSou
|
||||
addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
|
||||
addExternalIface('MozSmsMessage')
|
||||
addExternalIface('MozTreeView', nativeType='nsITreeView',
|
||||
headerFile='nsITreeView.h')
|
||||
headerFile='nsITreeView.h', notflattened=True)
|
||||
addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h')
|
||||
addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
|
||||
addExternalIface('nsIBrowserDOMWindow', nativeType='nsIBrowserDOMWindow',
|
||||
|
@ -6506,8 +6506,10 @@ class CGPerSignatureCall(CGThing):
|
||||
for i in descriptor.interface.getInheritedInterfaces())):
|
||||
cgThings.append(CGGeneric(dedent(
|
||||
"""
|
||||
if (mozilla::dom::CheckSafetyInPrerendering(cx, obj)) {
|
||||
//TODO: Handle call into unsafe API during Prerendering (Bug 730101)
|
||||
if (!mozilla::dom::EnforceNotInPrerendering(cx, obj)) {
|
||||
// Return false from the JSNative in order to trigger
|
||||
// an uncatchable exception.
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return false;
|
||||
}
|
||||
""")))
|
||||
|
@ -6,3 +6,8 @@
|
||||
[test_dom_xrays.html]
|
||||
[test_proxies_via_xray.html]
|
||||
[test_document_location_via_xray_cached.html]
|
||||
[test_blacklisted_prerendering_function.xul]
|
||||
support-files =
|
||||
file_focuser.html
|
||||
file_fullScreenPropertyAccessor.html
|
||||
skip-if = e10s # prerendering doesn't work in e10s yet
|
||||
|
24
dom/bindings/test/file_focuser.html
Normal file
24
dom/bindings/test/file_focuser.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<div id="stage"></div>
|
||||
<script>
|
||||
function stage(str) {
|
||||
var s = document.getElementById("stage");
|
||||
s.textContent = str;
|
||||
}
|
||||
stage("before");
|
||||
setTimeout(function() {
|
||||
stage("in timeout");
|
||||
});
|
||||
setInterval(function() {
|
||||
stage("in interval");
|
||||
});
|
||||
addEventListener("keydown", function() {
|
||||
stage("keydown");
|
||||
}, false);
|
||||
try {
|
||||
focus();
|
||||
stage("after");
|
||||
} catch(e) {
|
||||
stage("exception raised");
|
||||
}
|
||||
</script>
|
24
dom/bindings/test/file_fullScreenPropertyAccessor.html
Normal file
24
dom/bindings/test/file_fullScreenPropertyAccessor.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<div id="stage"></div>
|
||||
<script>
|
||||
function stage(str) {
|
||||
var s = document.getElementById("stage");
|
||||
s.textContent = str;
|
||||
}
|
||||
stage("before");
|
||||
setTimeout(function() {
|
||||
stage("in timeout");
|
||||
});
|
||||
setInterval(function() {
|
||||
stage("in interval");
|
||||
});
|
||||
addEventListener("keydown", function() {
|
||||
stage("keydown");
|
||||
}, false);
|
||||
try {
|
||||
window.fullScreen;
|
||||
stage("after");
|
||||
} catch(e) {
|
||||
stage("exception raised");
|
||||
}
|
||||
</script>
|
124
dom/bindings/test/test_blacklisted_prerendering_function.xul
Normal file
124
dom/bindings/test/test_blacklisted_prerendering_function.xul
Normal file
@ -0,0 +1,124 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
onload="runTest();">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function Listener(aBrowser, aPrerendered, aCallback) {
|
||||
this.init(aBrowser, aPrerendered, aCallback);
|
||||
}
|
||||
|
||||
Listener.prototype = {
|
||||
init: function(aBrowser, aPrerendered, aCallback) {
|
||||
this.mBrowser = aBrowser;
|
||||
this.mPrerendered = aPrerendered;
|
||||
this.mCallback = aCallback;
|
||||
},
|
||||
QueryInterface: function(aIID) {
|
||||
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
|
||||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
|
||||
aIID.equals(Components.interfaces.nsISupports))
|
||||
return this;
|
||||
throw Components.results.NS_NOINTERFACE;
|
||||
},
|
||||
onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
if ((aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) &&
|
||||
(aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_DOCUMENT)) {
|
||||
var doc = this.mBrowser.contentDocument;
|
||||
var stage = doc.getElementById("stage");
|
||||
if (this.mPrerendered) {
|
||||
is(stage.textContent, "before", "The blacklisted call should properly be intercepted in prerendering mode");
|
||||
} else {
|
||||
// In normal mode, we may or may not have run the timeout and/or the interval.
|
||||
switch (stage.textContent) {
|
||||
case "after":
|
||||
case "in timeout":
|
||||
case "in interval":
|
||||
ok(true, "The blacklisted call should work fine in normal mode");
|
||||
break;
|
||||
default:
|
||||
ok(false, "The blacklisted call should work fine in normal mode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
progress.removeProgressListener(progressListener);
|
||||
|
||||
// Set three timeouts to see if the interval triggered
|
||||
var self = this;
|
||||
function checkInterval() {
|
||||
var expected = self.mPrerendered ? "before" : "in interval";
|
||||
var desc = self.mPrerendered ? "No timer should be running" : "Timers should run as normal";
|
||||
is(stage.textContent, expected, desc);
|
||||
// Now, dispatch a key event to the window and see if the keydown handler runs
|
||||
synthesizeKey("a", {}, self.mBrowser.contentWindow);
|
||||
expected = self.mPrerendered ? "before" : "keydown";
|
||||
desc = self.mPrerendered ? "No event handler should be running" : "Event handlers should run as normal";
|
||||
is(stage.textContent, expected, desc);
|
||||
self.mCallback();
|
||||
}
|
||||
setTimeout(function() {
|
||||
setTimeout(function() {
|
||||
setTimeout(function() {
|
||||
checkInterval();
|
||||
}, 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
onProgressChange : function(aWebProgress, aRequest,
|
||||
aCurSelfProgress, aMaxSelfProgress,
|
||||
aCurTotalProgress, aMaxTotalProgress) {},
|
||||
onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags) {},
|
||||
onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {},
|
||||
onSecurityChange : function(aWebProgress, aRequest, aState) {},
|
||||
mBrowser: null,
|
||||
mPrerendered: false,
|
||||
mCallback: null
|
||||
};
|
||||
|
||||
var progress, progressListener;
|
||||
|
||||
function runTest() {
|
||||
testStep(false, "file_focuser.html", function() {
|
||||
testStep(true, "file_focuser.html", function() {
|
||||
testStep(false, "file_fullScreenPropertyAccessor.html", function() {
|
||||
testStep(true, "file_fullScreenPropertyAccessor.html", function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testStep(aPrerendered, aFileName, aCallback) {
|
||||
var browser = document.getElementById(aPrerendered ? "prerendered" : "normal");;
|
||||
progressListener = new Listener(browser, aPrerendered, aCallback);
|
||||
var docShell = browser.docShell;
|
||||
progress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIWebProgress);
|
||||
progress.addProgressListener(progressListener,
|
||||
Components.interfaces.nsIWebProgress.NOTIFY_ALL);
|
||||
browser.loadURI("chrome://mochitests/content/chrome/dom/bindings/test/" + aFileName);
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body id="html_body" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069719">Mozilla Bug 1069719</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
<browser prerendered="true" id="prerendered"/>
|
||||
<browser id="normal"/>
|
||||
</window>
|
@ -14,6 +14,7 @@
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
#include "mozilla/sandboxTarget.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "windows.h"
|
||||
#include <intrin.h>
|
||||
#include <assert.h>
|
||||
@ -33,6 +34,31 @@
|
||||
#include "sha256.h"
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
namespace {
|
||||
|
||||
// Scoped type used by Load
|
||||
struct ScopedActCtxHandleTraits
|
||||
{
|
||||
typedef HANDLE type;
|
||||
|
||||
static type empty()
|
||||
{
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
static void release(type aActCtxHandle)
|
||||
{
|
||||
if (aActCtxHandle != INVALID_HANDLE_VALUE) {
|
||||
ReleaseActCtx(aActCtxHandle);
|
||||
}
|
||||
}
|
||||
};
|
||||
typedef mozilla::Scoped<ScopedActCtxHandleTraits> ScopedActCtxHandle;
|
||||
|
||||
} // anonymous namespace
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
@ -168,6 +194,30 @@ GMPLoaderImpl::Load(const char* aLibPath,
|
||||
nodeId = std::string(aOriginSalt, aOriginSalt + aOriginSaltLen);
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
// If the GMP DLL is a side-by-side assembly with static imports then the DLL
|
||||
// loader will attempt to create an activation context which will fail because
|
||||
// of the sandbox. If we create an activation context before we start the
|
||||
// sandbox then this one will get picked up by the DLL loader.
|
||||
int pathLen = MultiByteToWideChar(CP_ACP, 0, aLibPath, -1, nullptr, 0);
|
||||
if (pathLen == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t* widePath = new wchar_t[pathLen];
|
||||
if (MultiByteToWideChar(CP_ACP, 0, aLibPath, -1, widePath, pathLen) == 0) {
|
||||
delete[] widePath;
|
||||
return false;
|
||||
}
|
||||
|
||||
ACTCTX actCtx = { sizeof(actCtx) };
|
||||
actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
|
||||
actCtx.lpSource = widePath;
|
||||
actCtx.lpResourceName = ISOLATIONAWARE_MANIFEST_RESOURCE_ID;
|
||||
ScopedActCtxHandle actCtxHandle(CreateActCtx(&actCtx));
|
||||
delete[] widePath;
|
||||
#endif
|
||||
|
||||
// Start the sandbox now that we've generated the device bound node id.
|
||||
// This must happen after the node id is bound to the device id, as
|
||||
// generating the device id requires privileges.
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prlog.h"
|
||||
#include <time.h>
|
||||
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
@ -36,14 +37,37 @@ extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
#define MSE_API(...)
|
||||
#endif
|
||||
|
||||
// RangeRemoval must be synchronous if appendBuffer is also synchronous.
|
||||
// While waiting for bug 1118589 to land, ensure RangeRemoval is synchronous
|
||||
#define APPENDBUFFER_IS_SYNCHRONOUS
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AppendDataRunnable : public nsRunnable {
|
||||
public:
|
||||
AppendDataRunnable(SourceBuffer* aSourceBuffer,
|
||||
const uint8_t* aData,
|
||||
uint32_t aLength,
|
||||
double aTimestampOffset)
|
||||
: mSourceBuffer(aSourceBuffer)
|
||||
, mTimestampOffset(aTimestampOffset)
|
||||
{
|
||||
mData.AppendElements(aData, aLength);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
|
||||
|
||||
mSourceBuffer->AppendData(mData.Elements(),
|
||||
mData.Length(),
|
||||
mTimestampOffset);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<SourceBuffer> mSourceBuffer;
|
||||
nsTArray<uint8_t> mData;
|
||||
double mTimestampOffset;
|
||||
};
|
||||
|
||||
class RangeRemovalRunnable : public nsRunnable {
|
||||
public:
|
||||
RangeRemovalRunnable(SourceBuffer* aSourceBuffer,
|
||||
@ -240,18 +264,8 @@ SourceBuffer::RangeRemoval(double aStart, double aEnd)
|
||||
{
|
||||
StartUpdating();
|
||||
|
||||
#if defined(APPENDBUFFER_IS_SYNCHRONOUS)
|
||||
DoRangeRemoval(aStart, aEnd);
|
||||
|
||||
// Run the final step of the buffer append algorithm asynchronously to
|
||||
// ensure the SourceBuffer's updating flag transition behaves as required
|
||||
// by the spec.
|
||||
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
|
||||
NS_DispatchToMainThread(event);
|
||||
#else
|
||||
nsRefPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd);
|
||||
NS_DispatchToMainThread(task);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -267,9 +281,7 @@ SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
|
||||
mTrackBuffer->RangeRemoval(start, end);
|
||||
}
|
||||
|
||||
#if !defined(APPENDBUFFER_IS_SYNCHRONOUS)
|
||||
StopUpdating();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -405,10 +417,29 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
|
||||
MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments,
|
||||
"We don't handle timestampOffset for sequence mode yet");
|
||||
nsRefPtr<nsIRunnable> task =
|
||||
new AppendDataRunnable(this, aData, aLength, mTimestampOffset);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, double aTimestampOffset)
|
||||
{
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
//
|
||||
// If the sequence appendBuffer(), abort(), appendBuffer() occurs before
|
||||
// the first StopUpdating() runnable runs, then a second StopUpdating()
|
||||
// runnable will be scheduled, but still only one (the first) will queue
|
||||
// events.
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mMediaSource);
|
||||
|
||||
if (aLength) {
|
||||
if (!mTrackBuffer->AppendData(aData, aLength, mTimestampOffset * USECS_PER_S)) {
|
||||
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethodWithArg<bool>(this, &SourceBuffer::AppendError, true);
|
||||
NS_DispatchToMainThread(event);
|
||||
if (!mTrackBuffer->AppendData(aData, aLength, aTimestampOffset * USECS_PER_S)) {
|
||||
AppendError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -419,12 +450,8 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
CheckEndTime();
|
||||
}
|
||||
|
||||
// Run the final step of the buffer append algorithm asynchronously to
|
||||
// ensure the SourceBuffer's updating flag transition behaves as required
|
||||
// by the spec.
|
||||
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
StopUpdating();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendError(bool aDecoderError)
|
||||
|
@ -122,6 +122,7 @@ private:
|
||||
~SourceBuffer();
|
||||
|
||||
friend class AsyncEventRunner<SourceBuffer>;
|
||||
friend class AppendDataRunnable;
|
||||
void DispatchSimpleEvent(const char* aName);
|
||||
void QueueAsyncSimpleEvent(const char* aName);
|
||||
|
||||
@ -137,6 +138,8 @@ private:
|
||||
|
||||
// Shared implementation of AppendBuffer overloads.
|
||||
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
|
||||
void AppendData(const uint8_t* aData, uint32_t aLength,
|
||||
double aTimestampOffset);
|
||||
|
||||
// Implement the "Append Error Algorithm".
|
||||
// Will call endOfStream() with "decode" error if aDecodeError is true.
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "prlog.h"
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* GetMediaSourceLog();
|
||||
|
@ -405,16 +405,16 @@ partial interface Window {
|
||||
|
||||
[Func="IsChromeOrXBL"]
|
||||
interface ChromeWindow {
|
||||
[Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
const unsigned short STATE_MAXIMIZED = 1;
|
||||
[Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
const unsigned short STATE_MINIMIZED = 2;
|
||||
[Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
const unsigned short STATE_NORMAL = 3;
|
||||
[Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
const unsigned short STATE_FULLSCREEN = 4;
|
||||
|
||||
[Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
readonly attribute unsigned short windowState;
|
||||
|
||||
/**
|
||||
@ -422,40 +422,40 @@ interface ChromeWindow {
|
||||
* utility functions implemented by chrome script. It will be null
|
||||
* for DOMWindows not corresponding to browsers.
|
||||
*/
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
attribute nsIBrowserDOMWindow? browserDOMWindow;
|
||||
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
void getAttention();
|
||||
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
void getAttentionWithCycleCount(long aCycleCount);
|
||||
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
void setCursor(DOMString cursor);
|
||||
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow", UnsafeInPrerendering]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow", UnsafeInPrerendering]
|
||||
void maximize();
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow", UnsafeInPrerendering]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow", UnsafeInPrerendering]
|
||||
void minimize();
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow", UnsafeInPrerendering]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow", UnsafeInPrerendering]
|
||||
void restore();
|
||||
|
||||
/**
|
||||
* Notify a default button is loaded on a dialog or a wizard.
|
||||
* defaultButton is the default button.
|
||||
*/
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
void notifyDefaultButtonLoaded(Element defaultButton);
|
||||
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
readonly attribute nsIMessageBroadcaster messageManager;
|
||||
|
||||
/**
|
||||
* Returns the message manager identified by the given group name that
|
||||
* manages all frame loaders belonging to that group.
|
||||
*/
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
nsIMessageBroadcaster getGroupMessageManager(DOMString aGroup);
|
||||
|
||||
/**
|
||||
@ -468,7 +468,7 @@ interface ChromeWindow {
|
||||
*
|
||||
* Throws NS_ERROR_NOT_IMPLEMENTED if the OS doesn't support this.
|
||||
*/
|
||||
[Throws, Func="nsGlobalWindow::IsChromeWindow"]
|
||||
[Throws, Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
void beginWindowMove(Event mouseDownEvent, optional Element? panel = null);
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_ServiceWorkerRegistration_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h"
|
||||
#include "mozilla/dom/ServiceWorkerCommon.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
@ -1100,8 +1100,7 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoun
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> sameOriginChecker = nsContentUtils::GetSameOriginChecker();
|
||||
NS_ENSURE_TRUE(sameOriginChecker, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsCOMPtr<nsIInterfaceRequestor> sameOriginChecker = nsContentUtils::SameOriginChecker();
|
||||
|
||||
channel->SetNotificationCallbacks(sameOriginChecker);
|
||||
|
||||
|
@ -434,11 +434,7 @@ XMLDocument::Load(const nsAString& aUrl, ErrorResult& aRv)
|
||||
mListenerManager = elm;
|
||||
|
||||
// Create a channel
|
||||
nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
|
||||
if (!req) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::SameOriginChecker();
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
// nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,
|
||||
|
@ -108,7 +108,7 @@ public:
|
||||
}
|
||||
|
||||
// We're not COM-y, so we don't get refcounts by default
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder, MOZ_OVERRIDE)
|
||||
|
||||
// Implement IResumable.
|
||||
virtual void Resume() MOZ_OVERRIDE;
|
||||
|
@ -453,7 +453,7 @@ AsmJSModule::setAutoFlushICacheRange()
|
||||
static void
|
||||
AsmJSReportOverRecursed()
|
||||
{
|
||||
JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
|
||||
JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
|
||||
js_ReportOverRecursed(cx);
|
||||
}
|
||||
|
||||
@ -461,14 +461,14 @@ static void
|
||||
OnDetached()
|
||||
{
|
||||
// See hasDetachedHeap comment in LinkAsmJS.
|
||||
JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
|
||||
JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
static bool
|
||||
AsmJSHandleExecutionInterrupt()
|
||||
{
|
||||
AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
|
||||
AsmJSActivation *act = JSRuntime::innermostAsmJSActivation();
|
||||
act->module().setInterrupted(true);
|
||||
bool ret = CheckForInterrupt(act->cx());
|
||||
act->module().setInterrupted(false);
|
||||
@ -478,7 +478,7 @@ AsmJSHandleExecutionInterrupt()
|
||||
static int32_t
|
||||
CoerceInPlace_ToInt32(MutableHandleValue val)
|
||||
{
|
||||
JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
|
||||
JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
|
||||
|
||||
int32_t i32;
|
||||
if (!ToInt32(cx, val, &i32))
|
||||
@ -491,7 +491,7 @@ CoerceInPlace_ToInt32(MutableHandleValue val)
|
||||
static int32_t
|
||||
CoerceInPlace_ToNumber(MutableHandleValue val)
|
||||
{
|
||||
JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
|
||||
JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx();
|
||||
|
||||
double dbl;
|
||||
if (!ToNumber(cx, val, &dbl))
|
||||
@ -570,7 +570,7 @@ InvokeFromAsmJS(AsmJSActivation *activation, int32_t exitIndex, int32_t argc, Va
|
||||
static int32_t
|
||||
InvokeFromAsmJS_Ignore(int32_t exitIndex, int32_t argc, Value *argv)
|
||||
{
|
||||
AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
|
||||
AsmJSActivation *activation = JSRuntime::innermostAsmJSActivation();
|
||||
JSContext *cx = activation->cx();
|
||||
|
||||
RootedValue rval(cx);
|
||||
@ -582,7 +582,7 @@ InvokeFromAsmJS_Ignore(int32_t exitIndex, int32_t argc, Value *argv)
|
||||
static int32_t
|
||||
InvokeFromAsmJS_ToInt32(int32_t exitIndex, int32_t argc, Value *argv)
|
||||
{
|
||||
AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
|
||||
AsmJSActivation *activation = JSRuntime::innermostAsmJSActivation();
|
||||
JSContext *cx = activation->cx();
|
||||
|
||||
RootedValue rval(cx);
|
||||
@ -602,7 +602,7 @@ InvokeFromAsmJS_ToInt32(int32_t exitIndex, int32_t argc, Value *argv)
|
||||
static int32_t
|
||||
InvokeFromAsmJS_ToNumber(int32_t exitIndex, int32_t argc, Value *argv)
|
||||
{
|
||||
AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
|
||||
AsmJSActivation *activation = JSRuntime::innermostAsmJSActivation();
|
||||
JSContext *cx = activation->cx();
|
||||
|
||||
RootedValue rval(cx);
|
||||
|
@ -448,7 +448,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||
return false;
|
||||
AutoSetHandlingSignal handling(rt);
|
||||
|
||||
AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
|
||||
AsmJSActivation *activation = rt->asmJSActivationStack();
|
||||
if (!activation)
|
||||
return false;
|
||||
|
||||
@ -647,7 +647,7 @@ HandleMachException(JSRuntime *rt, const ExceptionRequest &request)
|
||||
if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
|
||||
return false;
|
||||
|
||||
AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
|
||||
AsmJSActivation *activation = rt->asmJSActivationStack();
|
||||
if (!activation)
|
||||
return false;
|
||||
|
||||
@ -861,7 +861,7 @@ HandleFault(int signum, siginfo_t *info, void *ctx)
|
||||
return false;
|
||||
AutoSetHandlingSignal handling(rt);
|
||||
|
||||
AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
|
||||
AsmJSActivation *activation = rt->asmJSActivationStack();
|
||||
if (!activation)
|
||||
return false;
|
||||
|
||||
@ -947,12 +947,12 @@ RedirectJitCodeToInterruptCheck(JSRuntime *rt, CONTEXT *context)
|
||||
{
|
||||
RedirectIonBackedgesToInterruptCheck(rt);
|
||||
|
||||
if (AsmJSActivation *activation = rt->mainThread.asmJSActivationStack()) {
|
||||
if (AsmJSActivation *activation = rt->asmJSActivationStack()) {
|
||||
const AsmJSModule &module = activation->module();
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
if (module.containsFunctionPC((void*)rt->mainThread.simulator()->get_pc()))
|
||||
rt->mainThread.simulator()->set_resume_pc(int32_t(module.interruptExit()));
|
||||
if (module.containsFunctionPC((void*)rt->simulator()->get_pc()))
|
||||
rt->simulator()->set_resume_pc(int32_t(module.interruptExit()));
|
||||
#endif
|
||||
|
||||
uint8_t **ppc = ContextToPC(context);
|
||||
|
@ -8504,25 +8504,21 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
|
||||
// The following is inlined:
|
||||
// JSContext *cx = activation->cx();
|
||||
// Activation *act = cx->mainThread().activation();
|
||||
// Activation *act = cx->runtime()->activation();
|
||||
// act.active_ = true;
|
||||
// act.prevJitTop_ = cx->mainThread().jitTop;
|
||||
// act.prevJitJSContext_ = cx->mainThread().jitJSContext;
|
||||
// cx->mainThread().jitJSContext = cx;
|
||||
// act.prevJitActivation_ = cx->mainThread().jitActivation;
|
||||
// cx->mainThread().jitActivation = act;
|
||||
// act.prevProfilingActivation_ = cx->mainThread().profilingActivation;
|
||||
// cx->mainThread().profilingActivation_ = act;
|
||||
// act.prevJitTop_ = cx->runtime()->jitTop;
|
||||
// act.prevJitJSContext_ = cx->runtime()->jitJSContext;
|
||||
// cx->runtime()->jitJSContext = cx;
|
||||
// act.prevJitActivation_ = cx->runtime()->jitActivation;
|
||||
// cx->runtime()->jitActivation = act;
|
||||
// act.prevProfilingActivation_ = cx->runtime()->profilingActivation;
|
||||
// cx->runtime()->profilingActivation_ = act;
|
||||
// On the ARM store8() uses the secondScratchReg (lr) as a temp.
|
||||
size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
|
||||
PerThreadData::offsetOfActivation();
|
||||
size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop);
|
||||
size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
|
||||
offsetof(PerThreadData, jitJSContext);
|
||||
size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) +
|
||||
offsetof(PerThreadData, jitActivation);
|
||||
size_t offsetOfProfilingActivation = offsetof(JSRuntime, mainThread) +
|
||||
PerThreadData::offsetOfProfilingActivation();
|
||||
size_t offsetOfActivation = JSRuntime::offsetOfActivation();
|
||||
size_t offsetOfJitTop = offsetof(JSRuntime, jitTop);
|
||||
size_t offsetOfJitJSContext = offsetof(JSRuntime, jitJSContext);
|
||||
size_t offsetOfJitActivation = offsetof(JSRuntime, jitActivation);
|
||||
size_t offsetOfProfilingActivation = JSRuntime::offsetOfProfilingActivation();
|
||||
masm.loadAsmJSActivation(reg0);
|
||||
masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3);
|
||||
masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
|
||||
@ -8531,26 +8527,26 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
// act.active_ = true;
|
||||
masm.store8(Imm32(1), Address(reg1, JitActivation::offsetOfActiveUint8()));
|
||||
|
||||
// act.prevJitTop_ = cx->mainThread().jitTop;
|
||||
// act.prevJitTop_ = cx->runtime()->jitTop;
|
||||
masm.loadPtr(Address(reg0, offsetOfJitTop), reg2);
|
||||
masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitTop()));
|
||||
|
||||
// act.prevJitJSContext_ = cx->mainThread().jitJSContext;
|
||||
// act.prevJitJSContext_ = cx->runtime()->jitJSContext;
|
||||
masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2);
|
||||
masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext()));
|
||||
// cx->mainThread().jitJSContext = cx;
|
||||
// cx->runtime()->jitJSContext = cx;
|
||||
masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext));
|
||||
|
||||
// act.prevJitActivation_ = cx->mainThread().jitActivation;
|
||||
// act.prevJitActivation_ = cx->runtime()->jitActivation;
|
||||
masm.loadPtr(Address(reg0, offsetOfJitActivation), reg2);
|
||||
masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitActivation()));
|
||||
// cx->mainThread().jitActivation = act;
|
||||
// cx->runtime()->jitActivation = act;
|
||||
masm.storePtr(reg1, Address(reg0, offsetOfJitActivation));
|
||||
|
||||
// act.prevProfilingActivation_ = cx->mainThread().profilingActivation;
|
||||
// act.prevProfilingActivation_ = cx->runtime()->profilingActivation;
|
||||
masm.loadPtr(Address(reg0, offsetOfProfilingActivation), reg2);
|
||||
masm.storePtr(reg2, Address(reg1, Activation::offsetOfPrevProfiling()));
|
||||
// cx->mainThread().profilingActivation_ = act;
|
||||
// cx->runtime()->profilingActivation_ = act;
|
||||
masm.storePtr(reg1, Address(reg0, offsetOfProfilingActivation));
|
||||
}
|
||||
|
||||
@ -8571,41 +8567,37 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
|
||||
Register reg2 = AsmJSIonExitRegD2;
|
||||
|
||||
// The following is inlined:
|
||||
// rt->mainThread.profilingActivation = prevProfilingActivation_;
|
||||
// rt->mainThread.activation()->active_ = false;
|
||||
// rt->mainThread.jitTop = prevJitTop_;
|
||||
// rt->mainThread.jitJSContext = prevJitJSContext_;
|
||||
// rt->mainThread.jitActivation = prevJitActivation_;
|
||||
// rt->profilingActivation = prevProfilingActivation_;
|
||||
// rt->activation()->active_ = false;
|
||||
// rt->jitTop = prevJitTop_;
|
||||
// rt->jitJSContext = prevJitJSContext_;
|
||||
// rt->jitActivation = prevJitActivation_;
|
||||
// On the ARM store8() uses the secondScratchReg (lr) as a temp.
|
||||
size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
|
||||
PerThreadData::offsetOfActivation();
|
||||
size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop);
|
||||
size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
|
||||
offsetof(PerThreadData, jitJSContext);
|
||||
size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) +
|
||||
offsetof(PerThreadData, jitActivation);
|
||||
size_t offsetOfProfilingActivation = offsetof(JSRuntime, mainThread) +
|
||||
PerThreadData::offsetOfProfilingActivation();
|
||||
size_t offsetOfActivation = JSRuntime::offsetOfActivation();
|
||||
size_t offsetOfJitTop = offsetof(JSRuntime, jitTop);
|
||||
size_t offsetOfJitJSContext = offsetof(JSRuntime, jitJSContext);
|
||||
size_t offsetOfJitActivation = offsetof(JSRuntime, jitActivation);
|
||||
size_t offsetOfProfilingActivation = JSRuntime::offsetOfProfilingActivation();
|
||||
|
||||
masm.movePtr(AsmJSImmPtr(AsmJSImm_Runtime), reg0);
|
||||
masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
|
||||
|
||||
// rt->mainThread.jitTop = prevJitTop_;
|
||||
// rt->jitTop = prevJitTop_;
|
||||
masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitTop()), reg2);
|
||||
masm.storePtr(reg2, Address(reg0, offsetOfJitTop));
|
||||
|
||||
// rt->mainThread.profilingActivation = rt->mainThread.activation()->prevProfiling_;
|
||||
// rt->profilingActivation = rt->activation()->prevProfiling_;
|
||||
masm.loadPtr(Address(reg1, Activation::offsetOfPrevProfiling()), reg2);
|
||||
masm.storePtr(reg2, Address(reg0, offsetOfProfilingActivation));
|
||||
|
||||
// rt->mainThread.activation()->active_ = false;
|
||||
// rt->activation()->active_ = false;
|
||||
masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8()));
|
||||
|
||||
// rt->mainThread.jitJSContext = prevJitJSContext_;
|
||||
// rt->jitJSContext = prevJitJSContext_;
|
||||
masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitJSContext()), reg2);
|
||||
masm.storePtr(reg2, Address(reg0, offsetOfJitJSContext));
|
||||
|
||||
// rt->mainThread.jitActivation = prevJitActivation_;
|
||||
// rt->jitActivation = prevJitActivation_;
|
||||
masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitActivation()), reg2);
|
||||
masm.storePtr(reg2, Address(reg0, offsetOfJitActivation));
|
||||
}
|
||||
|
@ -1353,11 +1353,11 @@ js::testingFunc_assertFloat32(JSContext *cx, unsigned argc, jsval *vp)
|
||||
}
|
||||
|
||||
static bool
|
||||
TestingFunc_assertValidJitStack(JSContext *cx, unsigned argc, jsval *vp)
|
||||
TestingFunc_assertJitStackInvariants(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
jit::AssertValidJitStack(cx);
|
||||
jit::AssertJitStackInvariants(cx);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
@ -2499,8 +2499,8 @@ gc::ZealModeHelpText),
|
||||
"bailout()",
|
||||
" Force a bailout out of ionmonkey (if running in ionmonkey)."),
|
||||
|
||||
JS_FN_HELP("assertValidJitStack", TestingFunc_assertValidJitStack, 0, 0,
|
||||
"assertValidJitStack()",
|
||||
JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
|
||||
"assertJitStackInvariants()",
|
||||
" Iterates the Jit stack and check that stack invariants hold."),
|
||||
|
||||
JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
|
||||
|
@ -1,39 +1,49 @@
|
||||
# Spidermonkey JSAPI rooting analysis
|
||||
|
||||
This directory contains scripts and a makefile for running Brian Hackett's
|
||||
static GC rooting analysis on a JS source directory.
|
||||
|
||||
To use it:
|
||||
To use it on SpiderMonkey:
|
||||
|
||||
1. Download and compile sixgill. Make sure the gcc plugin is enabled. (The
|
||||
configure output will tell you.)
|
||||
1. Be on Fedora/CentOS/RedHat Linux x86_64.
|
||||
|
||||
- [sfink] I needed a couple of patches to get it work on Fedora 16/17/18.
|
||||
Ask me if you need them.
|
||||
(Specifically, the prebuilt GCC **won't work on Ubuntu**
|
||||
without the `CFLAGS` and `CXXFLAGS` settings from
|
||||
http://trac.wildfiregames.com/wiki/StaticRootingAnalysis .)
|
||||
|
||||
2. Compile an optimized JS shell that includes the patch at
|
||||
<http://people.mozilla.org/~sfink/data/bug-835552-cwd-snarf>. This does not
|
||||
need to be in the same source tree as you are running these scripts from.
|
||||
Remember the full path to the resulting JS binary; we'll call it $JS_SHELL
|
||||
below.
|
||||
2. Have the Gecko build prerequisites installed.
|
||||
|
||||
3. |make clean| in the objdir of the JS source tree that you're going to be
|
||||
analyzing. (These analysis scripts will default to the tree they are within,
|
||||
but you can point them at another tree.)
|
||||
3. In this directory, run these commands.
|
||||
|
||||
4. in $objdir/js/src/devtools/analysis, |make JS=$JS_SHELL
|
||||
SIXGILL=.../path/to/sixgill...|. You may need one or more of the following
|
||||
additional settings in addition to the |JS| already given:
|
||||
mkdir builddir
|
||||
cd builddir
|
||||
../run-analysis.sh
|
||||
|
||||
- JSOBJDIR: if you are analyzing a different source tree, set this to the
|
||||
objdir of the tree you want to analyze.
|
||||
`run-analysis.sh` is kind of like `configure` and `make` combined:
|
||||
the build directory can be wherever you want
|
||||
and you can name it whatever you want.
|
||||
(You could just run it right here in the source tree, and it would work,
|
||||
but don't do that -- it spits out files all over the place and
|
||||
then you'd have to clean up your source tree later.)
|
||||
|
||||
- ANALYSIS_SCRIPT_DIR: by default, the *.js files within the directory
|
||||
containing this README will be used, but you can point to a different
|
||||
directory full. At the time of this writing, there are some changes not in
|
||||
bhackett's git repo that are necessary, and you'll also need the
|
||||
gen-hazards.sh shell script.
|
||||
Output goes to `hazards.txt` in the builddir.
|
||||
|
||||
- JOBS: set this to the number of parallel jobs you'd like to run the final
|
||||
analysis pass with. This defaults to 6, somewhat randomly, which gave me a
|
||||
large speedup even on a machine with only 2 cores.
|
||||
To use this analysis on any other codebase,
|
||||
make a copy of `run-analysis.sh` and adapt it for your code.
|
||||
|
||||
|
||||
## Overview of what is going on here
|
||||
|
||||
So what does `run-analysis.sh` actually do?
|
||||
|
||||
1. **It insecurely downloads software over HTTP.** Yeah.
|
||||
See `run-analysis.sh` for details.
|
||||
|
||||
2. It runs `run_complete`, a Perl script, which builds the target
|
||||
codebase with a custom hacked GCC, generating a few database files
|
||||
containing (among other data) the full call graph.
|
||||
|
||||
3. Then it runs `analyze.py`, a Python script, which runs all the scripts
|
||||
which actually perform the analysis -- the tricky parts.
|
||||
(Those scripts are written in JS.)
|
||||
|
||||
The results will be in rootingHazards.txt
|
||||
|
85
js/src/devtools/rootAnalysis/run-analysis.sh
Executable file
85
js/src/devtools/rootAnalysis/run-analysis.sh
Executable file
@ -0,0 +1,85 @@
|
||||
# setup.sh - Run the rooting analysis on SpiderMonkey. See `README.txt` for usage.
|
||||
#
|
||||
# This script is based on the wiki page:
|
||||
# http://trac.wildfiregames.com/wiki/StaticRootingAnalysis
|
||||
|
||||
set -eu
|
||||
|
||||
BUILD_DIR="$PWD"
|
||||
ANALYSIS_SCRIPTDIR="$(dirname $0)"
|
||||
MOZILLA_SRCDIR="$(cd $ANALYSIS_SCRIPTDIR && (hg root || git rev-parse --show-toplevel))"
|
||||
|
||||
|
||||
# Requirements
|
||||
# ============
|
||||
#
|
||||
# Download and unpack the Sixgill plugin binaries.
|
||||
# (`wget -c` skips the download if you've already got the file.)
|
||||
#
|
||||
# This insecurely downloads software over HTTP. Sorry.
|
||||
#
|
||||
# The alternative is building your own Sixgill. That can be a pain and you may
|
||||
# need some patches to get it to work on your Linux distribution. Ask sfink for
|
||||
# details.
|
||||
|
||||
mkdir -p downloads
|
||||
(cd downloads && wget -c http://people.mozilla.org/~sfink/data/hazards-sixgill.tar.xz)
|
||||
tar xf downloads/hazards-sixgill.tar.xz
|
||||
|
||||
# Download and unpack GCC binaries compatible with the Sixgill plugin.
|
||||
(cd downloads && wget -c http://people.mozilla.org/~sfink/data/hazards-gcc4.7.tar.xz)
|
||||
tar xf downloads/hazards-gcc4.7.tar.xz
|
||||
|
||||
|
||||
# Generate raw data (.xdb files)
|
||||
# ==============================
|
||||
#
|
||||
# The first step is to generate the .xdb files that contain the information
|
||||
# needed by the analysis. This is done by compiling SpiderMonkey with the
|
||||
# sixgill plugin enabled. The plugin creates .xdb files which the analysis
|
||||
# consumes.
|
||||
|
||||
PATH=$BUILD_DIR/sixgill/usr/bin:$PATH
|
||||
export PATH
|
||||
GCCDIR=$BUILD_DIR/gcc/bin
|
||||
export GCCDIR
|
||||
|
||||
# Create a SpiderMonkey build directory and run configure.
|
||||
mkdir -p spidermonkey-analysis
|
||||
(cd spidermonkey-analysis && \
|
||||
$MOZILLA_SRCDIR/js/src/configure --enable-optimize)
|
||||
|
||||
# Make SpiderMonkey.
|
||||
$MOZILLA_SRCDIR/js/src/devtools/rootAnalysis/run_complete \
|
||||
--build-root=$BUILD_DIR/spidermonkey-analysis \
|
||||
--binaries=$BUILD_DIR/sixgill/usr/bin \
|
||||
--wrap-dir=$BUILD_DIR/sixgill/usr/libexec/sixgill/scripts/wrap_gcc \
|
||||
--buildcommand='make' \
|
||||
--foreground \
|
||||
--no-logs \
|
||||
.
|
||||
|
||||
|
||||
# Run the analysis
|
||||
# ================
|
||||
|
||||
# Build *another* copy of SpiderMonkey, using the system C++ compiler, without
|
||||
# Sixgill. This is what we use to run the analysis. (We don't let you skip this
|
||||
# step by setting a $JS environment variable or something, because you need
|
||||
# ctypes. Relax and spin a build. Get yourself a cup of tea.)
|
||||
mkdir -p spidermonkey-opt
|
||||
(cd spidermonkey-opt && \
|
||||
$MOZILLA_SRCDIR/js/src/configure --enable-optimize --enable-ctypes --enable-nspr-build && \
|
||||
make -j8)
|
||||
JS="$BUILD_DIR/spidermonkey-opt/dist/bin/js"
|
||||
|
||||
# Write a config file used by analyze.py.
|
||||
rm -f defaults.py
|
||||
echo "objdir = '${BUILD_DIR}/spidermonkey-analysis'" >> defaults.py
|
||||
echo "sixgill = '${BUILD_DIR}/sixgill/usr/libexec/sixgill'" >> defaults.py
|
||||
echo "sixgill_bin = '${BUILD_DIR}/sixgill/usr/bin'" >> defaults.py
|
||||
echo "js = '${JS}'" >> defaults.py
|
||||
echo "analysis_scriptdir = '${ANALYSIS_SCRIPTDIR}'" >> defaults.py
|
||||
|
||||
# Run the script that runs the scripts that do the analysis.
|
||||
python2.7 "${MOZILLA_SRCDIR}/js/src/devtools/rootAnalysis/analyze.py" -j 8 callgraph
|
@ -152,7 +152,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx, bool match_only)
|
||||
|
||||
// Check if we have space on the stack.
|
||||
Label stack_ok;
|
||||
void *stack_limit = runtime->mainThread.addressOfJitStackLimit();
|
||||
void *stack_limit = runtime->addressOfJitStackLimit();
|
||||
masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok);
|
||||
|
||||
// Exit with an exception. There is not enough space on the stack
|
||||
@ -262,7 +262,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx, bool match_only)
|
||||
}
|
||||
|
||||
// Initialize backtrack stack pointer.
|
||||
masm.loadPtr(AbsoluteAddress(runtime->mainThread.regexpStack.addressOfBase()), backtrack_stack_pointer);
|
||||
masm.loadPtr(AbsoluteAddress(runtime->regexpStack.addressOfBase()), backtrack_stack_pointer);
|
||||
masm.storePtr(backtrack_stack_pointer, Address(StackPointer, offsetof(FrameData, backtrackStackBase)));
|
||||
|
||||
masm.jump(&start_label_);
|
||||
@ -422,7 +422,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx, bool match_only)
|
||||
Address backtrackStackBaseAddress(temp2, offsetof(FrameData, backtrackStackBase));
|
||||
masm.subPtr(backtrackStackBaseAddress, backtrack_stack_pointer);
|
||||
|
||||
masm.loadPtr(AbsoluteAddress(runtime->mainThread.regexpStack.addressOfBase()), temp1);
|
||||
masm.loadPtr(AbsoluteAddress(runtime->regexpStack.addressOfBase()), temp1);
|
||||
masm.storePtr(temp1, backtrackStackBaseAddress);
|
||||
masm.addPtr(temp1, backtrack_stack_pointer);
|
||||
|
||||
@ -1050,7 +1050,7 @@ NativeRegExpMacroAssembler::CheckBacktrackStackLimit()
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckBacktrackStackLimit");
|
||||
|
||||
const void *limitAddr = runtime->mainThread.regexpStack.addressOfLimit();
|
||||
const void *limitAddr = runtime->regexpStack.addressOfLimit();
|
||||
|
||||
Label no_stack_overflow;
|
||||
masm.branchPtr(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr),
|
||||
|
@ -36,7 +36,7 @@ using namespace js;
|
||||
using namespace js::irregexp;
|
||||
|
||||
RegExpStackScope::RegExpStackScope(JSRuntime *rt)
|
||||
: regexp_stack(&rt->mainThread.regexpStack)
|
||||
: regexp_stack(&rt->regexpStack)
|
||||
{}
|
||||
|
||||
RegExpStackScope::~RegExpStackScope()
|
||||
@ -47,7 +47,7 @@ RegExpStackScope::~RegExpStackScope()
|
||||
int
|
||||
irregexp::GrowBacktrackStack(JSRuntime *rt)
|
||||
{
|
||||
return rt->mainThread.regexpStack.grow();
|
||||
return rt->regexpStack.grow();
|
||||
}
|
||||
|
||||
RegExpStack::RegExpStack()
|
||||
|
38
js/src/jit-test/tests/asm.js/bug1122338.js
Normal file
38
js/src/jit-test/tests/asm.js/bug1122338.js
Normal file
@ -0,0 +1,38 @@
|
||||
// The bug was that the asm.js linker did not catch that an unshared
|
||||
// view was created on a shared buffer. (Nor did it catch the vice
|
||||
// versa case.) As a consequence the code was not rejected (and run
|
||||
// as plain JS) as it should be. That gave rise to a difference in
|
||||
// output.
|
||||
|
||||
// Original test
|
||||
|
||||
g = (function(stdlib, n, heap) {
|
||||
"use asm";
|
||||
var Float32ArrayView = new stdlib.Float32Array(heap);
|
||||
function f() {
|
||||
return +Float32ArrayView[0]
|
||||
}
|
||||
return f
|
||||
})(this, {}, new SharedArrayBuffer(4096));
|
||||
assertEq(g(), NaN);
|
||||
|
||||
// Additional test: vice versa case.
|
||||
|
||||
try {
|
||||
g = (function(stdlib, n, heap) {
|
||||
"use asm";
|
||||
var Float32ArrayView = new stdlib.SharedFloat32Array(heap);
|
||||
function f() {
|
||||
return +Float32ArrayView[0]
|
||||
}
|
||||
return f
|
||||
})(this, {}, new ArrayBuffer(4096));
|
||||
// If the code runs, as it would with the bug present, it must return NaN.
|
||||
assertEq(g(), NaN);
|
||||
}
|
||||
catch (e) {
|
||||
// If the code throws then that's OK too. The current (January 2015)
|
||||
// semantics is for the SharedFloat32Array constructor to throw if
|
||||
// handed an ArrayBuffer, but asm.js does not execute that constructor
|
||||
// and before the fix it would not throw.
|
||||
}
|
7
js/src/jit-test/tests/debug/resumption-error-01.js
Normal file
7
js/src/jit-test/tests/debug/resumption-error-01.js
Normal file
@ -0,0 +1,7 @@
|
||||
// A resumption value can't have both {return:} and {throw:} properties.
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = Debugger(g);
|
||||
dbg.onDebuggerStatement = stack => ({return: 1, throw: 2});
|
||||
dbg.uncaughtExceptionHook = exc => ({return: "corrected"});
|
||||
assertEq(g.eval("debugger; false;"), "corrected");
|
16
js/src/jit-test/tests/debug/resumption-error-02.js
Normal file
16
js/src/jit-test/tests/debug/resumption-error-02.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Error handling if parsing a resumption value throws.
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = Debugger(g);
|
||||
var rv;
|
||||
dbg.onDebuggerStatement = stack => rv;
|
||||
dbg.uncaughtExceptionHook = function (exc) {
|
||||
assertEq(exc, "BANG");
|
||||
return {return: "recovered"};
|
||||
};
|
||||
|
||||
rv = {get throw() { throw "BANG"; }};
|
||||
assertEq(g.eval("debugger; false;"), "recovered");
|
||||
|
||||
rv = new Proxy({}, {has() { throw "BANG"; }});
|
||||
assertEq(g.eval("debugger; false;"), "recovered");
|
108
js/src/jit-test/tests/ion/simd-unbox.js
Normal file
108
js/src/jit-test/tests/ion/simd-unbox.js
Normal file
@ -0,0 +1,108 @@
|
||||
if (typeof SIMD === "undefined")
|
||||
quit();
|
||||
|
||||
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||
var max = 40, pivot = 35;
|
||||
|
||||
var i32x4 = SIMD.int32x4;
|
||||
var f32x4 = SIMD.float32x4;
|
||||
var i32x4Add = SIMD.int32x4.add;
|
||||
|
||||
var FakeSIMDType = function (o) { this.x = o.x; this.y = o.y; this.z = o.z; this.w = o.w; };
|
||||
if (this.hasOwnProperty("TypedObject")) {
|
||||
var TO = TypedObject;
|
||||
FakeSIMDType = new TO.StructType({ x: TO.int32, y: TO.int32, z: TO.int32, w: TO.int32 });
|
||||
}
|
||||
|
||||
|
||||
function simdunbox_bail_undef(i, lhs, rhs) {
|
||||
return i32x4Add(lhs, rhs);
|
||||
}
|
||||
|
||||
function simdunbox_bail_object(i, lhs, rhs) {
|
||||
return i32x4Add(lhs, rhs);
|
||||
}
|
||||
|
||||
function simdunbox_bail_typeobj(i, lhs, rhs) {
|
||||
return i32x4Add(lhs, rhs);
|
||||
}
|
||||
|
||||
function simdunbox_bail_badsimd(i, lhs, rhs) {
|
||||
return i32x4Add(lhs, rhs);
|
||||
}
|
||||
|
||||
var arr_undef = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||
var fail_undef = 0;
|
||||
var arr_object = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||
var fail_object = 0;
|
||||
var arr_typeobj = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||
var fail_typeobj = 0;
|
||||
var arr_badsimd = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||
var fail_badsimd = 0;
|
||||
for (var i = 0; i < max; i++) {
|
||||
try {
|
||||
arr_undef[i + 2] = simdunbox_bail_undef(i, arr_undef[i], arr_undef[i + 1]);
|
||||
} catch (x) {
|
||||
arr_undef[i + 2] = arr_undef[i - 1];
|
||||
fail_undef++;
|
||||
}
|
||||
|
||||
try {
|
||||
arr_object[i + 2] = simdunbox_bail_object(i, arr_object[i], arr_object[i + 1]);
|
||||
} catch (x) {
|
||||
arr_object[i + 2] = arr_object[i - 1];
|
||||
fail_object++;
|
||||
}
|
||||
|
||||
try {
|
||||
arr_typeobj[i + 2] = simdunbox_bail_typeobj(i, arr_typeobj[i], arr_typeobj[i + 1]);
|
||||
} catch (x) {
|
||||
arr_typeobj[i + 2] = arr_typeobj[i - 1];
|
||||
fail_typeobj++;
|
||||
}
|
||||
|
||||
try {
|
||||
arr_badsimd[i + 2] = simdunbox_bail_badsimd(i, arr_badsimd[i], arr_badsimd[i + 1]);
|
||||
} catch (x) {
|
||||
arr_badsimd[i + 2] = arr_badsimd[i - 1];
|
||||
fail_badsimd++;
|
||||
}
|
||||
|
||||
if (i + 2 == pivot) {
|
||||
arr_undef[pivot] = undefined;
|
||||
arr_object[pivot] = { x: 0, y: 1, z: 2, w: 3 };
|
||||
arr_typeobj[pivot] = new FakeSIMDType({ x: 0, y: 1, z: 2, w: 3 });
|
||||
arr_badsimd[pivot] = f32x4(0, 1, 2, 3);
|
||||
}
|
||||
}
|
||||
|
||||
assertEq(fail_undef, 2);
|
||||
assertEq(fail_object, 2);
|
||||
assertEq(fail_typeobj, 2);
|
||||
assertEq(fail_badsimd, 2);
|
||||
|
||||
// Assert that all SIMD values are correct.
|
||||
function assertEqX4(real, expected, assertFunc) {
|
||||
if (typeof assertFunc === 'undefined')
|
||||
assertFunc = assertEq;
|
||||
|
||||
assertFunc(real.x, expected[0]);
|
||||
assertFunc(real.y, expected[1]);
|
||||
assertFunc(real.z, expected[2]);
|
||||
assertFunc(real.w, expected[3]);
|
||||
}
|
||||
|
||||
var fib = [0, 1];
|
||||
for (i = 0; i < max + 5; i++)
|
||||
fib[i+2] = (fib[i] + fib[i+1]) | 0;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
if (i == pivot)
|
||||
continue;
|
||||
var ref = fib.slice(i < pivot ? i : i - 3);
|
||||
assertEqX4(arr_undef[i], ref);
|
||||
assertEqX4(arr_object[i], ref);
|
||||
assertEqX4(arr_typeobj[i], ref);
|
||||
assertEqX4(arr_badsimd[i], ref);
|
||||
}
|
@ -4,11 +4,33 @@ var i;
|
||||
|
||||
// Check that an entry frame is always aligned properly.
|
||||
function entryFrame_1() {
|
||||
assertValidJitStack();
|
||||
assertJitStackInvariants();
|
||||
}
|
||||
|
||||
// Check rectifier frames are keeping the same alignment.
|
||||
function rectifierFrame_verify(a, b, c, d) {
|
||||
assertJitStackInvariants();
|
||||
}
|
||||
|
||||
function rectifierFrame_1(i) {
|
||||
rectifierFrame_verify();
|
||||
}
|
||||
function rectifierFrame_2(i) {
|
||||
rectifierFrame_verify(i);
|
||||
}
|
||||
function rectifierFrame_3(i) {
|
||||
rectifierFrame_verify(i, i);
|
||||
}
|
||||
function rectifierFrame_4(i) {
|
||||
rectifierFrame_verify(i, i, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < 40; i++) {
|
||||
entryFrame_1();
|
||||
entryFrame_1(0);
|
||||
entryFrame_1(0, 1);
|
||||
rectifierFrame_1(i);
|
||||
rectifierFrame_2(i);
|
||||
rectifierFrame_3(i);
|
||||
rectifierFrame_4(i);
|
||||
}
|
||||
|
16
js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js
Normal file
16
js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Getting a property that exists on an ordinary object
|
||||
// does not touch a proxy on its proto chain.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var angryHandler = new Proxy({}, {
|
||||
get(t, id) { throw new Error("angryHandler should not be queried (" + id + ")"); }
|
||||
});
|
||||
var angryProto = new Proxy({}, angryHandler);
|
||||
var obj = Object.create(angryProto, {
|
||||
x: {value: 3},
|
||||
y: {get: () => 4}
|
||||
});
|
||||
assertThrowsInstanceOf(() => obj.z, Error); // check that angryProto works
|
||||
assertEq(obj.x, 3);
|
||||
assertEq(obj.y, 4);
|
31
js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js
Normal file
31
js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js
Normal file
@ -0,0 +1,31 @@
|
||||
// Getting a property that's inherted from a proxy calls the proxy's get handler.
|
||||
|
||||
var handler = {
|
||||
get(t, id, r) {
|
||||
assertEq(this, handler);
|
||||
assertEq(t, target);
|
||||
assertEq(id, "foo");
|
||||
assertEq(r, obj);
|
||||
return "bar";
|
||||
},
|
||||
getOwnPropertyDescriptor(t, id) {
|
||||
throw "FAIL";
|
||||
}
|
||||
};
|
||||
|
||||
var target = {};
|
||||
var proto = new Proxy(target, handler);
|
||||
var obj = Object.create(proto);
|
||||
assertEq(obj.foo, "bar");
|
||||
|
||||
// Longer proto chain: same result.
|
||||
var origObj = obj;
|
||||
for (var i = 0; i < 4; i++)
|
||||
obj = Object.create(obj);
|
||||
assertEq(obj.foo, "bar");
|
||||
|
||||
// Chain of transparent proxy wrappers: same result.
|
||||
obj = origObj;
|
||||
for (var i = 0; i < 4; i++)
|
||||
obj = new Proxy(obj, {});
|
||||
assertEq(obj.foo, "bar");
|
21
js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js
Normal file
21
js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Recursion through the get hook works; runaway recursion is checked.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var hits = 0, limit = 10;
|
||||
var proto = new Proxy({}, {
|
||||
get(t, id, r) {
|
||||
assertEq(r, obj);
|
||||
if (hits++ >= limit)
|
||||
return "ding";
|
||||
return obj[id];
|
||||
}
|
||||
});
|
||||
|
||||
var obj = Object.create(proto);
|
||||
assertEq(obj.prop, "ding");
|
||||
|
||||
hits = 0;
|
||||
limit = Infinity;
|
||||
assertThrowsInstanceOf(() => obj.prop, InternalError);
|
||||
assertEq(hits > 10, true);
|
@ -0,0 +1,6 @@
|
||||
// A proxy P whose target is an object X whose prototype is an array V inherits V.length.
|
||||
|
||||
var V = [1, 2, 3];
|
||||
var X = Object.create(V);
|
||||
var P = new Proxy(X, {});
|
||||
assertEq(P.length, 3);
|
18
js/src/jit-test/tests/proxy/testWrapperGetInherited.js
Normal file
18
js/src/jit-test/tests/proxy/testWrapperGetInherited.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Getting a property O.X, inherited from a transparent cross-compartment wrapper W
|
||||
// that wraps a Proxy P.
|
||||
|
||||
var g = newGlobal();
|
||||
var target = {}
|
||||
var P = new Proxy(target, {
|
||||
get(t, id, r) {
|
||||
assertEq(t, target);
|
||||
assertEq(id, "X");
|
||||
assertEq(r, wO);
|
||||
return "vega";
|
||||
}
|
||||
});
|
||||
|
||||
g.W = P;
|
||||
g.eval("var O = Object.create(W);");
|
||||
var wO = g.O;
|
||||
assertEq(g.eval("O.X"), "vega");
|
@ -101,6 +101,7 @@ BacktrackingAllocator::go()
|
||||
dumpRegisterGroups();
|
||||
|
||||
JitSpew(JitSpew_RegAlloc, "Beginning main allocation loop");
|
||||
|
||||
// Allocate, spill and split register intervals until finished.
|
||||
while (!allocationQueue.empty()) {
|
||||
if (mir->shouldCancel("Backtracking Allocation"))
|
||||
@ -112,10 +113,22 @@ BacktrackingAllocator::go()
|
||||
}
|
||||
JitSpew(JitSpew_RegAlloc, "Main allocation loop complete");
|
||||
|
||||
if (!pickStackSlots())
|
||||
return false;
|
||||
|
||||
if (JitSpewEnabled(JitSpew_RegAlloc))
|
||||
dumpAllocations();
|
||||
|
||||
return resolveControlFlow() && reifyAllocations() && populateSafepoints();
|
||||
if (!resolveControlFlow())
|
||||
return false;
|
||||
|
||||
if (!reifyAllocations())
|
||||
return false;
|
||||
|
||||
if (!populateSafepoints())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -383,6 +396,7 @@ BacktrackingAllocator::groupAndQueueRegisters()
|
||||
// for that slot, and reuse it for other registers in the group.
|
||||
LDefinition *def = reg.def();
|
||||
if (def->policy() == LDefinition::FIXED && !def->output()->isRegister()) {
|
||||
MOZ_ASSERT(!def->output()->isStackSlot());
|
||||
reg.setCanonicalSpill(*def->output());
|
||||
if (reg.group() && reg.group()->spill.isUse())
|
||||
reg.group()->spill = *def->output();
|
||||
@ -930,10 +944,11 @@ BacktrackingAllocator::spill(LiveInterval *interval)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t stackSlot = stackSlotAllocator.allocateSlot(reg->type());
|
||||
MOZ_ASSERT(stackSlot <= stackSlotAllocator.stackHeight());
|
||||
uint32_t virtualSlot = numVirtualStackSlots++;
|
||||
|
||||
LStackSlot alloc(stackSlot);
|
||||
// Count virtual stack slots down from the maximum representable value, so
|
||||
// that virtual slots are more obviously distinguished from real slots.
|
||||
LStackSlot alloc(LAllocation::DATA_MASK - virtualSlot);
|
||||
interval->setAllocation(alloc);
|
||||
|
||||
JitSpew(JitSpew_RegAlloc, " Allocating spill location %s", alloc.toString());
|
||||
@ -945,6 +960,179 @@ BacktrackingAllocator::spill(LiveInterval *interval)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BacktrackingAllocator::pickStackSlots()
|
||||
{
|
||||
for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
|
||||
BacktrackingVirtualRegister *reg = &vregs[i];
|
||||
|
||||
if (mir->shouldCancel("Backtracking Pick Stack Slots"))
|
||||
return false;
|
||||
|
||||
for (size_t j = 0; j < reg->numIntervals(); j++) {
|
||||
LiveInterval *interval = reg->getInterval(j);
|
||||
if (!pickStackSlot(interval))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BacktrackingAllocator::pickStackSlot(LiveInterval *interval)
|
||||
{
|
||||
LAllocation alloc = *interval->getAllocation();
|
||||
MOZ_ASSERT(!alloc.isUse());
|
||||
|
||||
if (!isVirtualStackSlot(alloc))
|
||||
return true;
|
||||
|
||||
BacktrackingVirtualRegister ® = vregs[interval->vreg()];
|
||||
|
||||
// Get a list of all the intervals which will share this stack slot.
|
||||
LiveIntervalVector commonIntervals;
|
||||
|
||||
if (!commonIntervals.append(interval))
|
||||
return false;
|
||||
|
||||
if (reg.canonicalSpill() && alloc == *reg.canonicalSpill()) {
|
||||
// Look for other intervals in the vreg using this spill slot.
|
||||
for (size_t i = 0; i < reg.numIntervals(); i++) {
|
||||
LiveInterval *ninterval = reg.getInterval(i);
|
||||
if (ninterval != interval && *ninterval->getAllocation() == alloc) {
|
||||
if (!commonIntervals.append(ninterval))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for intervals in other registers with the same group using this
|
||||
// spill slot.
|
||||
if (reg.group() && alloc == reg.group()->spill) {
|
||||
for (size_t i = 0; i < reg.group()->registers.length(); i++) {
|
||||
uint32_t nvreg = reg.group()->registers[i];
|
||||
if (nvreg == interval->vreg())
|
||||
continue;
|
||||
BacktrackingVirtualRegister &nreg = vregs[nvreg];
|
||||
for (size_t j = 0; j < nreg.numIntervals(); j++) {
|
||||
LiveInterval *ninterval = nreg.getInterval(j);
|
||||
if (*ninterval->getAllocation() == alloc) {
|
||||
if (!commonIntervals.append(ninterval))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT_IF(reg.group(), alloc != reg.group()->spill);
|
||||
}
|
||||
|
||||
if (!reuseOrAllocateStackSlot(commonIntervals, reg.type(), &alloc))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(!isVirtualStackSlot(alloc));
|
||||
|
||||
// Set the physical stack slot for each of the intervals found earlier.
|
||||
for (size_t i = 0; i < commonIntervals.length(); i++)
|
||||
commonIntervals[i]->setAllocation(alloc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BacktrackingAllocator::reuseOrAllocateStackSlot(const LiveIntervalVector &intervals, LDefinition::Type type,
|
||||
LAllocation *palloc)
|
||||
{
|
||||
SpillSlotList *slotList;
|
||||
switch (StackSlotAllocator::width(type)) {
|
||||
case 4: slotList = &normalSlots; break;
|
||||
case 8: slotList = &doubleSlots; break;
|
||||
case 16: slotList = &quadSlots; break;
|
||||
default:
|
||||
MOZ_CRASH("Bad width");
|
||||
}
|
||||
|
||||
// Maximum number of existing spill slots we will look at before giving up
|
||||
// and allocating a new slot.
|
||||
static const size_t MAX_SEARCH_COUNT = 10;
|
||||
|
||||
if (!slotList->empty()) {
|
||||
size_t searches = 0;
|
||||
SpillSlot *stop = nullptr;
|
||||
while (true) {
|
||||
SpillSlot *spill = *slotList->begin();
|
||||
if (!stop) {
|
||||
stop = spill;
|
||||
} else if (stop == spill) {
|
||||
// We looked through every slot in the list.
|
||||
break;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
for (size_t i = 0; i < intervals.length() && success; i++) {
|
||||
LiveInterval *interval = intervals[i];
|
||||
for (size_t j = 0; j < interval->numRanges(); j++) {
|
||||
AllocatedRange range(interval, interval->getRange(j)), existing;
|
||||
if (spill->allocated.contains(range, &existing)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
// We can reuse this physical stack slot for the new intervals.
|
||||
// Update the allocated ranges for the slot.
|
||||
if (!insertAllRanges(spill->allocated, intervals))
|
||||
return false;
|
||||
*palloc = spill->alloc;
|
||||
return true;
|
||||
}
|
||||
|
||||
// On a miss, move the spill to the end of the list. This will cause us
|
||||
// to make fewer attempts to allocate from slots with a large and
|
||||
// highly contended range.
|
||||
slotList->popFront();
|
||||
slotList->pushBack(spill);
|
||||
|
||||
if (++searches == MAX_SEARCH_COUNT)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We need a new physical stack slot.
|
||||
uint32_t stackSlot = stackSlotAllocator.allocateSlot(type);
|
||||
|
||||
// Make sure the virtual and physical stack slots don't start overlapping.
|
||||
if (isVirtualStackSlot(LStackSlot(stackSlot)))
|
||||
return false;
|
||||
|
||||
SpillSlot *spill = new(alloc()) SpillSlot(stackSlot, alloc().lifoAlloc());
|
||||
if (!spill)
|
||||
return false;
|
||||
|
||||
if (!insertAllRanges(spill->allocated, intervals))
|
||||
return false;
|
||||
|
||||
*palloc = spill->alloc;
|
||||
|
||||
slotList->pushFront(spill);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BacktrackingAllocator::insertAllRanges(AllocatedRangeSet &set, const LiveIntervalVector &intervals)
|
||||
{
|
||||
for (size_t i = 0; i < intervals.length(); i++) {
|
||||
LiveInterval *interval = intervals[i];
|
||||
for (size_t j = 0; j < interval->numRanges(); j++) {
|
||||
AllocatedRange range(interval, interval->getRange(j));
|
||||
if (!set.insert(range))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add moves to resolve conflicting assignments between a block and its
|
||||
// predecessors. XXX try to common this with LinearScanAllocator.
|
||||
bool
|
||||
|
@ -176,9 +176,28 @@ class BacktrackingAllocator
|
||||
// should be prioritized.
|
||||
AllocatedRangeSet hotcode;
|
||||
|
||||
// During register allocation, virtual stack slots are used for spills.
|
||||
// These are converted to actual spill locations
|
||||
size_t numVirtualStackSlots;
|
||||
|
||||
// Information about an allocated stack slot.
|
||||
struct SpillSlot : public TempObject, public InlineForwardListNode<SpillSlot> {
|
||||
LStackSlot alloc;
|
||||
AllocatedRangeSet allocated;
|
||||
|
||||
SpillSlot(uint32_t slot, LifoAlloc *alloc)
|
||||
: alloc(slot), allocated(alloc)
|
||||
{}
|
||||
};
|
||||
typedef InlineForwardList<SpillSlot> SpillSlotList;
|
||||
|
||||
// All allocated slots of each width.
|
||||
SpillSlotList normalSlots, doubleSlots, quadSlots;
|
||||
|
||||
public:
|
||||
BacktrackingAllocator(MIRGenerator *mir, LIRGenerator *lir, LIRGraph &graph)
|
||||
: LiveRangeAllocator<BacktrackingVirtualRegister, /* forLSRA = */ false>(mir, lir, graph)
|
||||
: LiveRangeAllocator<BacktrackingVirtualRegister, /* forLSRA = */ false>(mir, lir, graph),
|
||||
numVirtualStackSlots(0)
|
||||
{ }
|
||||
|
||||
bool go();
|
||||
@ -213,7 +232,12 @@ class BacktrackingAllocator
|
||||
bool addLiveInterval(LiveIntervalVector &intervals, uint32_t vreg,
|
||||
LiveInterval *spillInterval,
|
||||
CodePosition from, CodePosition to);
|
||||
bool pickStackSlot(LiveInterval *interval);
|
||||
bool reuseOrAllocateStackSlot(const LiveIntervalVector &intervals, LDefinition::Type type,
|
||||
LAllocation *palloc);
|
||||
bool insertAllRanges(AllocatedRangeSet &set, const LiveIntervalVector &intervals);
|
||||
|
||||
bool pickStackSlots();
|
||||
bool resolveControlFlow();
|
||||
bool reifyAllocations();
|
||||
bool populateSafepoints();
|
||||
@ -249,6 +273,11 @@ class BacktrackingAllocator
|
||||
bool compilingAsmJS() {
|
||||
return mir->info().compilingAsmJS();
|
||||
}
|
||||
|
||||
bool isVirtualStackSlot(LAllocation alloc) {
|
||||
return alloc.isStackSlot() &&
|
||||
LAllocation::DATA_MASK - alloc.toStackSlot()->slot() < numVirtualStackSlots;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
@ -34,7 +34,7 @@ jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
|
||||
MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) &&
|
||||
IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
|
||||
"Fake jitTop pointer should be within the first page.");
|
||||
cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
|
||||
cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
|
||||
|
||||
JitActivationIterator jitActivations(cx->runtime());
|
||||
BailoutFrameInfo bailoutData(jitActivations, sp);
|
||||
@ -94,7 +94,7 @@ jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
|
||||
// In both cases, we want to temporarily set the |lastProfilingFrame|
|
||||
// to the current frame being bailed out, and then fix it up later.
|
||||
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
|
||||
cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr);
|
||||
cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -108,7 +108,7 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
|
||||
JSContext *cx = GetJSContextFromJitCode();
|
||||
|
||||
// We don't have an exit frame.
|
||||
cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
|
||||
cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
|
||||
|
||||
JitActivationIterator jitActivations(cx->runtime());
|
||||
BailoutFrameInfo bailoutData(jitActivations, sp);
|
||||
@ -168,7 +168,7 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
|
||||
|
||||
// Make the frame being bailed out the top profiled frame.
|
||||
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
|
||||
cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr);
|
||||
cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -197,7 +197,7 @@ jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
|
||||
// operation callback like a timeout handler.
|
||||
MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
|
||||
|
||||
cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
|
||||
cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
|
||||
gc::AutoSuppressGC suppress(cx);
|
||||
|
||||
JitActivationIterator jitActivations(cx->runtime());
|
||||
@ -238,7 +238,7 @@ jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
|
||||
|
||||
// Make the frame being bailed out the top profiled frame.
|
||||
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
|
||||
cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr);
|
||||
cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -1654,7 +1654,7 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
|
||||
// (which must be a baseline frame), and set it as the last profiling
|
||||
// frame.
|
||||
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
|
||||
cx->mainThread().jitActivation->setLastProfilingFrame(iter.prevFp());
|
||||
cx->runtime()->jitActivation->setLastProfilingFrame(iter.prevFp());
|
||||
|
||||
uint32_t frameno = 0;
|
||||
while (frameno < numFrames) {
|
||||
@ -1707,7 +1707,7 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
|
||||
// values into the baseline frame. We need to do this even when debug mode
|
||||
// is off, as we should respect the mutations made while debug mode was
|
||||
// on.
|
||||
JitActivation *act = cx->mainThread().activation()->asJit();
|
||||
JitActivation *act = cx->runtime()->activation()->asJit();
|
||||
if (act->hasRematerializedFrame(outerFp)) {
|
||||
JitFrameIterator iter(cx);
|
||||
size_t inlineDepth = numFrames;
|
||||
@ -1754,6 +1754,8 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
|
||||
case Bailout_NonObjectInput:
|
||||
case Bailout_NonStringInput:
|
||||
case Bailout_NonSymbolInput:
|
||||
case Bailout_NonSimdInt32x4Input:
|
||||
case Bailout_NonSimdFloat32x4Input:
|
||||
case Bailout_InitialState:
|
||||
case Bailout_Debugger:
|
||||
// Do nothing.
|
||||
|
@ -512,7 +512,7 @@ bool
|
||||
BaselineCompiler::emitStackCheck(bool earlyCheck)
|
||||
{
|
||||
Label skipCall;
|
||||
void *limitAddr = cx->runtime()->mainThread.addressOfJitStackLimit();
|
||||
void *limitAddr = cx->runtime()->addressOfJitStackLimit();
|
||||
uint32_t slotsSize = script->nslots() * sizeof(Value);
|
||||
uint32_t tolerance = earlyCheck ? slotsSize : 0;
|
||||
|
||||
@ -3644,7 +3644,7 @@ BaselineCompiler::emit_JSOP_RESUME()
|
||||
Label skip;
|
||||
AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
|
||||
masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip);
|
||||
masm.loadPtr(AbsoluteAddress(cx->mainThread().addressOfProfilingActivation()), scratchReg);
|
||||
masm.loadPtr(AbsoluteAddress(cx->runtime()->addressOfProfilingActivation()), scratchReg);
|
||||
masm.storePtr(BaselineStackReg,
|
||||
Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()));
|
||||
masm.bind(&skip);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "builtin/Eval.h"
|
||||
#include "builtin/SIMD.h"
|
||||
#include "jit/BaselineDebugModeOSR.h"
|
||||
#include "jit/BaselineHelpers.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
@ -1013,7 +1014,7 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
Label checkOk;
|
||||
AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
|
||||
masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &checkOk);
|
||||
masm.loadPtr(AbsoluteAddress((void*)&cx->mainThread().jitActivation), scratchReg);
|
||||
masm.loadPtr(AbsoluteAddress((void*)&cx->runtime()->jitActivation), scratchReg);
|
||||
masm.loadPtr(Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()), scratchReg);
|
||||
|
||||
// It may be the case that we entered the baseline frame with
|
||||
@ -8840,7 +8841,7 @@ TryAttachFunCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script,
|
||||
|
||||
static bool
|
||||
GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc,
|
||||
Native native, const CallArgs &args, MutableHandleNativeObject res)
|
||||
Native native, const CallArgs &args, MutableHandleObject res)
|
||||
{
|
||||
// Check for natives to which template objects can be attached. This is
|
||||
// done to provide templates to Ion for inlining these natives later on.
|
||||
@ -8917,6 +8918,14 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (native == js::simd_int32x4_add && JitSupportsSimd()) {
|
||||
Rooted<TypeDescr *> descr(cx, &Int32x4::GetTypeDescr(*cx->global()));
|
||||
res.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap));
|
||||
if (!res)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -9151,7 +9160,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedNativeObject templateObject(cx);
|
||||
RootedObject templateObject(cx);
|
||||
if (MOZ_LIKELY(!isSpread)) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!GetTemplateObjectForNative(cx, script, pc, fun->native(), args, &templateObject))
|
||||
@ -11741,7 +11750,7 @@ ICCall_AnyScripted::Clone(JSContext *, ICStubSpace *space, ICStub *firstMonitorS
|
||||
}
|
||||
|
||||
ICCall_Native::ICCall_Native(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
HandleFunction callee, HandleNativeObject templateObject,
|
||||
HandleFunction callee, HandleObject templateObject,
|
||||
uint32_t pcOffset)
|
||||
: ICMonitoredStub(ICStub::Call_Native, stubCode, firstMonitorStub),
|
||||
callee_(callee),
|
||||
@ -11762,7 +11771,7 @@ ICCall_Native::Clone(JSContext *cx, ICStubSpace *space, ICStub *firstMonitorStub
|
||||
ICCall_Native &other)
|
||||
{
|
||||
RootedFunction callee(cx, other.callee_);
|
||||
RootedNativeObject templateObject(cx, other.templateObject_);
|
||||
RootedObject templateObject(cx, other.templateObject_);
|
||||
return New(space, other.jitCode(), firstMonitorStub, callee, templateObject,
|
||||
other.pcOffset_);
|
||||
}
|
||||
|
@ -5921,7 +5921,7 @@ class ICCall_Native : public ICMonitoredStub
|
||||
|
||||
protected:
|
||||
HeapPtrFunction callee_;
|
||||
HeapPtrNativeObject templateObject_;
|
||||
HeapPtrObject templateObject_;
|
||||
uint32_t pcOffset_;
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
@ -5929,12 +5929,12 @@ class ICCall_Native : public ICMonitoredStub
|
||||
#endif
|
||||
|
||||
ICCall_Native(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
HandleFunction callee, HandleNativeObject templateObject,
|
||||
HandleFunction callee, HandleObject templateObject,
|
||||
uint32_t pcOffset);
|
||||
|
||||
public:
|
||||
static inline ICCall_Native *New(ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub,
|
||||
HandleFunction callee, HandleNativeObject templateObject,
|
||||
HandleFunction callee, HandleObject templateObject,
|
||||
uint32_t pcOffset)
|
||||
{
|
||||
if (!code)
|
||||
@ -5949,7 +5949,7 @@ class ICCall_Native : public ICMonitoredStub
|
||||
HeapPtrFunction &callee() {
|
||||
return callee_;
|
||||
}
|
||||
HeapPtrNativeObject &templateObject() {
|
||||
HeapPtrObject &templateObject() {
|
||||
return templateObject_;
|
||||
}
|
||||
|
||||
@ -5973,7 +5973,7 @@ class ICCall_Native : public ICMonitoredStub
|
||||
bool isConstructing_;
|
||||
bool isSpread_;
|
||||
RootedFunction callee_;
|
||||
RootedNativeObject templateObject_;
|
||||
RootedObject templateObject_;
|
||||
uint32_t pcOffset_;
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
@ -5984,7 +5984,7 @@ class ICCall_Native : public ICMonitoredStub
|
||||
|
||||
public:
|
||||
Compiler(JSContext *cx, ICStub *firstMonitorStub,
|
||||
HandleFunction callee, HandleNativeObject templateObject,
|
||||
HandleFunction callee, HandleObject templateObject,
|
||||
bool isConstructing, bool isSpread, uint32_t pcOffset)
|
||||
: ICCallStubCompiler(cx, ICStub::Call_Native),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
|
@ -440,7 +440,7 @@ BaselineInspector::getTemplateObject(jsbytecode *pc)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NativeObject *
|
||||
JSObject *
|
||||
BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
|
@ -110,7 +110,7 @@ class BaselineInspector
|
||||
bool hasSeenNonStringIterMore(jsbytecode *pc);
|
||||
|
||||
NativeObject *getTemplateObject(jsbytecode *pc);
|
||||
NativeObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
|
||||
JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
|
||||
JSObject *getTemplateObjectForClassHook(jsbytecode *pc, const Class *clasp);
|
||||
|
||||
DeclEnvObject *templateDeclEnvObject();
|
||||
|
@ -3075,25 +3075,17 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
|
||||
|
||||
masm.checkStackAlignment();
|
||||
|
||||
// If the function is known to be uncompilable, only emit the call to InvokeFunction.
|
||||
if (apply->hasSingleTarget()) {
|
||||
JSFunction *target = apply->getSingleTarget();
|
||||
if (target->isNative()) {
|
||||
emitCallInvokeFunction(apply, copyreg);
|
||||
emitPopArguments(apply, copyreg);
|
||||
return;
|
||||
}
|
||||
// If the function is native, only emit the call to InvokeFunction.
|
||||
if (apply->hasSingleTarget() && apply->getSingleTarget()->isNative()) {
|
||||
emitCallInvokeFunction(apply, copyreg);
|
||||
emitPopArguments(apply, copyreg);
|
||||
return;
|
||||
}
|
||||
|
||||
Label end, invoke;
|
||||
|
||||
// Guard that calleereg is an interpreted function with a JSScript:
|
||||
if (!apply->hasSingleTarget()) {
|
||||
masm.branchIfFunctionHasNoScript(calleereg, &invoke);
|
||||
} else {
|
||||
// Native single targets are handled by LCallNative.
|
||||
MOZ_ASSERT(!apply->getSingleTarget()->isNative());
|
||||
}
|
||||
// Guard that calleereg is an interpreted function with a JSScript.
|
||||
masm.branchIfFunctionHasNoScript(calleereg, &invoke);
|
||||
|
||||
// Knowing that calleereg is a non-native function, load the JSScript.
|
||||
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
|
||||
@ -4233,6 +4225,74 @@ CodeGenerator::visitSimdBox(LSimdBox *lir)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitSimdUnbox(LSimdUnbox *lir)
|
||||
{
|
||||
Register object = ToRegister(lir->input());
|
||||
FloatRegister simd = ToFloatRegister(lir->output());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
Label bail;
|
||||
|
||||
// obj->type()
|
||||
masm.loadPtr(Address(object, JSObject::offsetOfType()), temp);
|
||||
|
||||
// Guard that the object has the same representation as the one produced for
|
||||
// SIMD value-type.
|
||||
Address clasp(temp, types::TypeObject::offsetOfClasp());
|
||||
static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent");
|
||||
masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_),
|
||||
&bail);
|
||||
|
||||
// obj->type()->typeDescr()
|
||||
// The previous class pointer comparison implies that the addendumKind is
|
||||
// Addendum_TypeDescr.
|
||||
masm.loadPtr(Address(temp, types::TypeObject::offsetOfAddendum()), temp);
|
||||
|
||||
// Check for the /Kind/ reserved slot of the TypeDescr. This is an Int32
|
||||
// Value which is equivalent to the object class check.
|
||||
static_assert(JS_DESCR_SLOT_KIND < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots");
|
||||
Address typeDescrKind(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_KIND));
|
||||
masm.assertTestInt32(Assembler::Equal, typeDescrKind,
|
||||
"MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_KIND).isInt32())");
|
||||
masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrKind), Imm32(js::type::Simd), &bail);
|
||||
|
||||
// Convert the SIMD MIRType to a SimdTypeDescr::Type.
|
||||
js::SimdTypeDescr::Type type;
|
||||
switch (lir->mir()->type()) {
|
||||
case MIRType_Int32x4:
|
||||
type = js::SimdTypeDescr::TYPE_INT32;
|
||||
break;
|
||||
case MIRType_Float32x4:
|
||||
type = js::SimdTypeDescr::TYPE_FLOAT32;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected SIMD Type.");
|
||||
}
|
||||
|
||||
// Check if the SimdTypeDescr /Type/ match the specialization of this
|
||||
// MSimdUnbox instruction.
|
||||
static_assert(JS_DESCR_SLOT_TYPE < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots");
|
||||
Address typeDescrType(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_TYPE));
|
||||
masm.assertTestInt32(Assembler::Equal, typeDescrType,
|
||||
"MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_TYPE).isInt32())");
|
||||
masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrType), Imm32(type), &bail);
|
||||
|
||||
// Load the value from the data of the InlineTypedObject.
|
||||
Address objectData(object, InlineTypedObject::offsetOfDataStart());
|
||||
switch (lir->mir()->type()) {
|
||||
case MIRType_Int32x4:
|
||||
masm.loadUnalignedInt32x4(objectData, simd);
|
||||
break;
|
||||
case MIRType_Float32x4:
|
||||
masm.loadUnalignedFloat32x4(objectData, simd);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("The impossible happened!");
|
||||
}
|
||||
|
||||
bailoutFrom(&bail, lir->snapshot());
|
||||
}
|
||||
|
||||
typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap);
|
||||
static const VMFunction NewDeclEnvObjectInfo =
|
||||
FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
|
||||
|
@ -156,6 +156,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitOutOfLineNewObject(OutOfLineNewObject *ool);
|
||||
void visitNewTypedObject(LNewTypedObject *lir);
|
||||
void visitSimdBox(LSimdBox *lir);
|
||||
void visitSimdUnbox(LSimdUnbox *lir);
|
||||
void visitNewDeclEnvObject(LNewDeclEnvObject *lir);
|
||||
void visitNewCallObject(LNewCallObject *lir);
|
||||
void visitNewSingletonCallObject(LNewSingletonCallObject *lir);
|
||||
|
@ -37,37 +37,37 @@ CompileRuntime::mainThread()
|
||||
const void *
|
||||
CompileRuntime::addressOfJitTop()
|
||||
{
|
||||
return &runtime()->mainThread.jitTop;
|
||||
return &runtime()->jitTop;
|
||||
}
|
||||
|
||||
const void *
|
||||
CompileRuntime::addressOfJitActivation()
|
||||
{
|
||||
return &runtime()->mainThread.jitActivation;
|
||||
return &runtime()->jitActivation;
|
||||
}
|
||||
|
||||
const void *
|
||||
CompileRuntime::addressOfProfilingActivation()
|
||||
{
|
||||
return (const void *) &runtime()->mainThread.profilingActivation_;
|
||||
return (const void *) &runtime()->profilingActivation_;
|
||||
}
|
||||
|
||||
const void *
|
||||
CompileRuntime::addressOfJitStackLimit()
|
||||
{
|
||||
return runtime()->mainThread.addressOfJitStackLimit();
|
||||
return runtime()->addressOfJitStackLimit();
|
||||
}
|
||||
|
||||
const void *
|
||||
CompileRuntime::addressOfJSContext()
|
||||
{
|
||||
return &runtime()->mainThread.jitJSContext;
|
||||
return &runtime()->jitJSContext;
|
||||
}
|
||||
|
||||
const void *
|
||||
CompileRuntime::addressOfActivation()
|
||||
{
|
||||
return runtime()->mainThread.addressOfActivation();
|
||||
return runtime()->addressOfActivation();
|
||||
}
|
||||
|
||||
const void *
|
||||
|
@ -31,22 +31,22 @@ class CompileRuntime
|
||||
|
||||
js::PerThreadData *mainThread();
|
||||
|
||||
// &mainThread.jitTop
|
||||
// &runtime()->jitTop
|
||||
const void *addressOfJitTop();
|
||||
|
||||
// &mainThread.jitActivation
|
||||
// &runtime()->jitActivation
|
||||
const void *addressOfJitActivation();
|
||||
|
||||
// &mainThread.profilingActivation
|
||||
// &runtime()->profilingActivation
|
||||
const void *addressOfProfilingActivation();
|
||||
|
||||
// rt->mainThread.jitStackLimit;
|
||||
// rt->runtime()->jitStackLimit;
|
||||
const void *addressOfJitStackLimit();
|
||||
|
||||
// &mainThread.jitJSContext
|
||||
// &runtime()->jitJSContext
|
||||
const void *addressOfJSContext();
|
||||
|
||||
// &mainThread.activation_
|
||||
// &runtime()->activation_
|
||||
const void *addressOfActivation();
|
||||
|
||||
// &GetJitContext()->runtime->nativeIterCache.last
|
||||
|
@ -788,7 +788,11 @@ class IonBuilder
|
||||
bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id,
|
||||
ScalarTypeDescr::Type *arrayType);
|
||||
InliningStatus inlineConstructTypedObject(CallInfo &callInfo, TypeDescr *target);
|
||||
|
||||
// SIMD intrinsics and natives.
|
||||
InliningStatus inlineConstructSimdObject(CallInfo &callInfo, SimdTypeDescr *target);
|
||||
InliningStatus inlineSimdInt32x4BinaryArith(CallInfo &callInfo, JSNative native,
|
||||
MSimdBinaryArith::Operation op);
|
||||
|
||||
// Utility intrinsics.
|
||||
InliningStatus inlineIsCallable(CallInfo &callInfo);
|
||||
|
@ -100,6 +100,10 @@ enum BailoutKind
|
||||
Bailout_NonStringInput,
|
||||
Bailout_NonSymbolInput,
|
||||
|
||||
// SIMD Unbox expects a given type, bails out if it doesn't match.
|
||||
Bailout_NonSimdInt32x4Input,
|
||||
Bailout_NonSimdFloat32x4Input,
|
||||
|
||||
// For the initial snapshot when entering a function.
|
||||
Bailout_InitialState,
|
||||
|
||||
@ -193,6 +197,10 @@ BailoutKindString(BailoutKind kind)
|
||||
return "Bailout_NonStringInput";
|
||||
case Bailout_NonSymbolInput:
|
||||
return "Bailout_NonSymbolInput";
|
||||
case Bailout_NonSimdInt32x4Input:
|
||||
return "Bailout_NonSimdInt32x4Input";
|
||||
case Bailout_NonSimdFloat32x4Input:
|
||||
return "Bailout_NonSimdFloat32x4Input";
|
||||
case Bailout_InitialState:
|
||||
return "Bailout_InitialState";
|
||||
case Bailout_Debugger:
|
||||
|
@ -89,8 +89,9 @@ class BaselineFrame;
|
||||
class JitActivation;
|
||||
|
||||
// Iterate over the JIT stack to assert that all invariants are respected.
|
||||
// - Check that all entry frames are aligned on StackAlignment.
|
||||
void AssertValidJitStack(JSContext *cx);
|
||||
// - Check that all entry frames are aligned on JitStackAlignment.
|
||||
// - Check that all rectifier frames keep the JitStackAlignment.
|
||||
void AssertJitStackInvariants(JSContext *cx);
|
||||
|
||||
class JitFrameIterator
|
||||
{
|
||||
|
@ -98,12 +98,12 @@ JitFrameIterator::JitFrameIterator()
|
||||
}
|
||||
|
||||
JitFrameIterator::JitFrameIterator(JSContext *cx)
|
||||
: current_(cx->perThreadData->jitTop),
|
||||
: current_(cx->runtime()->jitTop),
|
||||
type_(JitFrame_Exit),
|
||||
returnAddressToFp_(nullptr),
|
||||
frameSize_(0),
|
||||
cachedSafepointIndex_(nullptr),
|
||||
activation_(cx->perThreadData->activation()->asJit())
|
||||
activation_(cx->runtime()->activation()->asJit())
|
||||
{
|
||||
if (activation_->bailoutData()) {
|
||||
current_ = activation_->bailoutData()->fp();
|
||||
@ -390,7 +390,7 @@ HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromEx
|
||||
// Debugger has observed this frame (e.g., for onPop).
|
||||
bool shouldBail = Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind);
|
||||
if (!shouldBail) {
|
||||
JitActivation *act = cx->mainThread().activation()->asJit();
|
||||
JitActivation *act = cx->runtime()->activation()->asJit();
|
||||
RematerializedFrame *rematFrame =
|
||||
act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo());
|
||||
shouldBail = rematFrame && rematFrame->isDebuggee();
|
||||
@ -693,10 +693,10 @@ struct AutoResetLastProfilerFrameOnReturnFromException
|
||||
if (!cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(cx->mainThread().jitActivation == cx->mainThread().profilingActivation());
|
||||
MOZ_ASSERT(cx->runtime()->jitActivation == cx->runtime()->profilingActivation());
|
||||
|
||||
void *lastProfilingFrame = getLastProfilingFrame();
|
||||
cx->mainThread().jitActivation->setLastProfilingFrame(lastProfilingFrame);
|
||||
cx->runtime()->jitActivation->setLastProfilingFrame(lastProfilingFrame);
|
||||
}
|
||||
|
||||
void *getLastProfilingFrame() {
|
||||
@ -740,7 +740,7 @@ HandleException(ResumeFromException *rfe)
|
||||
if (cx->runtime()->jitRuntime()->hasIonReturnOverride())
|
||||
cx->runtime()->jitRuntime()->takeIonReturnOverride();
|
||||
|
||||
JitActivation *activation = cx->mainThread().activation()->asJit();
|
||||
JitActivation *activation = cx->runtime()->activation()->asJit();
|
||||
|
||||
// The Debugger onExceptionUnwind hook (reachable via
|
||||
// HandleExceptionBaseline below) may cause on-stack recompilation of
|
||||
@ -863,7 +863,7 @@ HandleException(ResumeFromException *rfe)
|
||||
// not crash when accessing an IonScript that's destroyed by the
|
||||
// ionScript->decref call.
|
||||
EnsureExitFrame(current);
|
||||
cx->mainThread().jitTop = (uint8_t *)current;
|
||||
cx->runtime()->jitTop = (uint8_t *)current;
|
||||
}
|
||||
|
||||
if (overrecursed) {
|
||||
@ -2737,16 +2737,16 @@ JitProfilingFrameIterator::JitProfilingFrameIterator(
|
||||
{
|
||||
// If no profilingActivation is live, initialize directly to
|
||||
// end-of-iteration state.
|
||||
if (!rt->mainThread.profilingActivation()) {
|
||||
if (!rt->profilingActivation()) {
|
||||
type_ = JitFrame_Entry;
|
||||
fp_ = nullptr;
|
||||
returnAddressToFp_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(rt->mainThread.profilingActivation()->isJit());
|
||||
MOZ_ASSERT(rt->profilingActivation()->isJit());
|
||||
|
||||
JitActivation *act = rt->mainThread.profilingActivation()->asJit();
|
||||
JitActivation *act = rt->profilingActivation()->asJit();
|
||||
|
||||
// If the top JitActivation has a null lastProfilingFrame, assume that
|
||||
// it's a trivially empty activation, and initialize directly
|
||||
@ -3026,12 +3026,30 @@ InvalidationBailoutStack::checkInvariants() const
|
||||
}
|
||||
|
||||
void
|
||||
AssertValidJitStack(JSContext *cx)
|
||||
AssertJitStackInvariants(JSContext *cx)
|
||||
{
|
||||
for (JitActivationIterator activations(cx->runtime()); !activations.done(); ++activations) {
|
||||
JitFrameIterator frames(activations);
|
||||
for (; !frames.done(); ++frames)
|
||||
continue;
|
||||
for (; !frames.done(); ++frames) {
|
||||
|
||||
if (frames.prevType() == JitFrame_Rectifier) {
|
||||
size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
|
||||
size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
|
||||
MOZ_ASSERT(callerFp >= calleeFp);
|
||||
size_t frameSize = callerFp - calleeFp;
|
||||
|
||||
MOZ_RELEASE_ASSERT(frameSize % JitStackAlignment == 0,
|
||||
"The rectifier frame should keep the alignment");
|
||||
|
||||
size_t expectedFrameSize = 0
|
||||
+ sizeof(Value) * (frames.callee()->nargs() + 1 /* |this| argument */ )
|
||||
+ sizeof(JitFrameLayout);
|
||||
MOZ_RELEASE_ASSERT(frameSize >= expectedFrameSize,
|
||||
"The frame is large enough to hold all arguments");
|
||||
MOZ_RELEASE_ASSERT(expectedFrameSize + JitStackAlignment > frameSize,
|
||||
"The frame size is optimal");
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(frames.type() == JitFrame_Entry,
|
||||
"The first frame of a Jit activation should be an entry frame");
|
||||
|
@ -157,6 +157,26 @@ class LSimdBox : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
class LSimdUnbox : public LInstructionHelper<1, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SimdUnbox)
|
||||
|
||||
LSimdUnbox(const LAllocation &obj, const LDefinition &temp)
|
||||
{
|
||||
setOperand(0, obj);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
MSimdUnbox *mir() const {
|
||||
return mir_->toSimdUnbox();
|
||||
}
|
||||
};
|
||||
|
||||
// Constructs a SIMD value with 4 equal components (e.g. int32x4, float32x4).
|
||||
class LSimdSplatX4 : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
|
@ -67,7 +67,6 @@ class LAllocation : public TempObject
|
||||
protected:
|
||||
static const uintptr_t DATA_BITS = (sizeof(uint32_t) * 8) - KIND_BITS;
|
||||
static const uintptr_t DATA_SHIFT = KIND_SHIFT + KIND_BITS;
|
||||
static const uintptr_t DATA_MASK = (1 << DATA_BITS) - 1;
|
||||
|
||||
public:
|
||||
enum Kind {
|
||||
@ -80,6 +79,8 @@ class LAllocation : public TempObject
|
||||
ARGUMENT_SLOT // Argument slot.
|
||||
};
|
||||
|
||||
static const uintptr_t DATA_MASK = (1 << DATA_BITS) - 1;
|
||||
|
||||
protected:
|
||||
uint32_t data() const {
|
||||
return uint32_t(bits_) >> DATA_SHIFT;
|
||||
|
@ -18,6 +18,7 @@
|
||||
_(Double) \
|
||||
_(Float32) \
|
||||
_(SimdBox) \
|
||||
_(SimdUnbox) \
|
||||
_(SimdSplatX4) \
|
||||
_(Int32x4) \
|
||||
_(Float32x4) \
|
||||
|
@ -3734,6 +3734,30 @@ LIRGenerator::visitSimdBox(MSimdBox *ins)
|
||||
define(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitSimdUnbox(MSimdUnbox *ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->input()->type() == MIRType_Object);
|
||||
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||
LUse in = useRegister(ins->input());
|
||||
|
||||
BailoutKind kind;
|
||||
switch (ins->type()) {
|
||||
case MIRType_Int32x4:
|
||||
kind = Bailout_NonSimdInt32x4Input;
|
||||
break;
|
||||
case MIRType_Float32x4:
|
||||
kind = Bailout_NonSimdFloat32x4Input;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected SIMD Type.");
|
||||
};
|
||||
|
||||
LSimdUnbox *lir = new(alloc()) LSimdUnbox(in, temp());
|
||||
assignSnapshot(lir, kind);
|
||||
define(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitSimdConstant(MSimdConstant *ins)
|
||||
{
|
||||
|
@ -266,6 +266,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitRecompileCheck(MRecompileCheck *ins);
|
||||
void visitMemoryBarrier(MMemoryBarrier *ins);
|
||||
void visitSimdBox(MSimdBox *ins);
|
||||
void visitSimdUnbox(MSimdUnbox *ins);
|
||||
void visitSimdExtractElement(MSimdExtractElement *ins);
|
||||
void visitSimdInsertElement(MSimdInsertElement *ins);
|
||||
void visitSimdSignMask(MSimdSignMask *ins);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "jsmath.h"
|
||||
|
||||
#include "builtin/AtomicsObject.h"
|
||||
#include "builtin/SIMD.h"
|
||||
#include "builtin/TestingFunctions.h"
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "jit/BaselineInspector.h"
|
||||
@ -246,6 +247,10 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
|
||||
if (native == js::CallOrConstructBoundFunction)
|
||||
return inlineBoundFunction(callInfo, target);
|
||||
|
||||
// Simd functions
|
||||
if (native == js::simd_int32x4_add)
|
||||
return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Add);
|
||||
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
@ -349,17 +354,17 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
||||
uint32_t initLength = 0;
|
||||
AllocatingBehaviour allocating = NewArray_Unallocating;
|
||||
|
||||
NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array);
|
||||
JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array);
|
||||
if (!templateObject)
|
||||
return InliningStatus_NotInlined;
|
||||
MOZ_ASSERT(templateObject->is<ArrayObject>());
|
||||
ArrayObject *templateArray = &templateObject->as<ArrayObject>();
|
||||
|
||||
// Multiple arguments imply array initialization, not just construction.
|
||||
if (callInfo.argc() >= 2) {
|
||||
initLength = callInfo.argc();
|
||||
allocating = NewArray_FullyAllocating;
|
||||
|
||||
types::TypeObjectKey *type = types::TypeObjectKey::get(templateObject);
|
||||
types::TypeObjectKey *type = types::TypeObjectKey::get(templateArray);
|
||||
if (!type->unknownProperties()) {
|
||||
types::HeapTypeSetKey elemTypes = type->property(JSID_VOID);
|
||||
|
||||
@ -376,9 +381,9 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
||||
types::TemporaryTypeSet::DoubleConversion conversion =
|
||||
getInlineReturnTypeSet()->convertDoubleElements(constraints());
|
||||
if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
|
||||
templateObject->setShouldConvertDoubleElements();
|
||||
templateArray->setShouldConvertDoubleElements();
|
||||
else
|
||||
templateObject->clearShouldConvertDoubleElements();
|
||||
templateArray->clearShouldConvertDoubleElements();
|
||||
|
||||
// A single integer argument denotes initial length.
|
||||
if (callInfo.argc() == 1) {
|
||||
@ -388,7 +393,6 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
||||
MDefinition *arg = callInfo.getArg(0);
|
||||
if (!arg->isConstantValue()) {
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
ArrayObject *templateArray = &templateObject->as<ArrayObject>();
|
||||
MNewArrayDynamicLength *ins =
|
||||
MNewArrayDynamicLength::New(alloc(), constraints(), templateArray,
|
||||
templateArray->type()->initialHeap(constraints()),
|
||||
@ -406,7 +410,7 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
||||
// Make sure initLength matches the template object's length. This is
|
||||
// not guaranteed to be the case, for instance if we're inlining the
|
||||
// MConstant may come from an outer script.
|
||||
if (initLength != templateObject->as<ArrayObject>().length())
|
||||
if (initLength != templateArray->as<ArrayObject>().length())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// Don't inline large allocations.
|
||||
@ -418,11 +422,11 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
|
||||
MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateArray);
|
||||
current->add(templateConst);
|
||||
|
||||
MNewArray *ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
|
||||
templateObject->type()->initialHeap(constraints()),
|
||||
templateArray->type()->initialHeap(constraints()),
|
||||
allocating);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
@ -730,7 +734,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo)
|
||||
}
|
||||
|
||||
// Inline the call.
|
||||
NativeObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
|
||||
JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
|
||||
if (!templateObj || templateObj->type() != baseThisType)
|
||||
return InliningStatus_NotInlined;
|
||||
MOZ_ASSERT(templateObj->is<ArrayObject>());
|
||||
@ -1611,7 +1615,7 @@ IonBuilder::inlineObjectCreate(CallInfo &callInfo)
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, obj_create);
|
||||
JSObject *templateObject = inspector->getTemplateObjectForNative(pc, obj_create);
|
||||
if (!templateObject)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
@ -2613,5 +2617,37 @@ IonBuilder::inlineConstructSimdObject(CallInfo &callInfo, SimdTypeDescr *descr)
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineSimdInt32x4BinaryArith(CallInfo &callInfo, JSNative native,
|
||||
MSimdBinaryArith::Operation op)
|
||||
{
|
||||
if (callInfo.argc() != 2)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
JSObject *templateObject = inspector->getTemplateObjectForNative(pc, native);
|
||||
if (!templateObject)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
InlineTypedObject *inlineTypedObject = &templateObject->as<InlineTypedObject>();
|
||||
MOZ_ASSERT(inlineTypedObject->typeDescr().as<SimdTypeDescr>().type() == js::Int32x4::type);
|
||||
|
||||
// If the type of any of the arguments is neither a SIMD type, an Object
|
||||
// type, or a Value, then the applyTypes phase will add a fallible box &
|
||||
// unbox sequence. This does not matter much as the binary arithmetic
|
||||
// instruction is supposed to produce a TypeError once it is called.
|
||||
MSimdBinaryArith *ins = MSimdBinaryArith::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
|
||||
op, MIRType_Int32x4);
|
||||
|
||||
MSimdBox *obj = MSimdBox::New(alloc(), constraints(), ins, inlineTypedObject,
|
||||
inlineTypedObject->type()->initialHeap(constraints()));
|
||||
|
||||
current->add(ins);
|
||||
current->add(obj);
|
||||
current->push(obj);
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -1922,7 +1922,7 @@ class MSimdBinaryComp
|
||||
|
||||
class MSimdBinaryArith
|
||||
: public MBinaryInstruction,
|
||||
public NoTypePolicy::Data
|
||||
public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
|
||||
{
|
||||
public:
|
||||
enum Operation {
|
||||
@ -1958,8 +1958,6 @@ class MSimdBinaryArith
|
||||
{
|
||||
MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Add || op == Sub || op == Mul);
|
||||
MOZ_ASSERT(IsSimdType(type));
|
||||
MOZ_ASSERT(left->type() == right->type());
|
||||
MOZ_ASSERT(left->type() == type);
|
||||
setResultType(type);
|
||||
setMovable();
|
||||
if (op == Add || op == Mul || op == Min || op == Max)
|
||||
@ -1968,10 +1966,18 @@ class MSimdBinaryArith
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SimdBinaryArith)
|
||||
static MSimdBinaryArith *New(TempAllocator &alloc, MDefinition *left, MDefinition *right,
|
||||
Operation op, MIRType t)
|
||||
{
|
||||
return new(alloc) MSimdBinaryArith(left, right, op, t);
|
||||
}
|
||||
|
||||
static MSimdBinaryArith *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right,
|
||||
Operation op, MIRType t)
|
||||
{
|
||||
return new(alloc) MSimdBinaryArith(left, right, op, t);
|
||||
MOZ_ASSERT(left->type() == right->type());
|
||||
MOZ_ASSERT(left->type() == t);
|
||||
return New(alloc, left, right, op, t);
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const MOZ_OVERRIDE {
|
||||
@ -2984,6 +2990,33 @@ class MSimdBox
|
||||
}
|
||||
};
|
||||
|
||||
class MSimdUnbox
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
protected:
|
||||
MSimdUnbox(MDefinition *op, MIRType type)
|
||||
: MUnaryInstruction(op)
|
||||
{
|
||||
MOZ_ASSERT(IsSimdType(type));
|
||||
setMovable();
|
||||
setResultType(type);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SimdUnbox)
|
||||
ALLOW_CLONE(MSimdUnbox)
|
||||
|
||||
static MSimdUnbox *New(TempAllocator &alloc, MDefinition *op, MIRType type)
|
||||
{
|
||||
return new(alloc) MSimdUnbox(op, type);
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const MOZ_OVERRIDE {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// Creates a new derived type object. At runtime, this is just a call
|
||||
// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
|
||||
// compile to particularly optimized code. However, using a distinct
|
||||
|
@ -13,6 +13,7 @@ namespace jit {
|
||||
#define MIR_OPCODE_LIST(_) \
|
||||
_(Constant) \
|
||||
_(SimdBox) \
|
||||
_(SimdUnbox) \
|
||||
_(SimdValueX4) \
|
||||
_(SimdSplatX4) \
|
||||
_(SimdConstant) \
|
||||
|
@ -1356,6 +1356,20 @@ MacroAssembler::assumeUnreachable(const char *output)
|
||||
breakpoint();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssembler::assertTestInt32(Condition cond, const T &value, const char *output)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Label ok;
|
||||
branchTestInt32(cond, value, &ok);
|
||||
assumeUnreachable(output);
|
||||
bind(&ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
template void MacroAssembler::assertTestInt32(Condition, const Address &, const char *);
|
||||
|
||||
static void
|
||||
Printf0_(const char *output) {
|
||||
// Use stderr instead of stdout because this is only used for debug
|
||||
|
@ -976,6 +976,10 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
void link(JitCode *code);
|
||||
|
||||
void assumeUnreachable(const char *output);
|
||||
|
||||
template<typename T>
|
||||
void assertTestInt32(Condition cond, const T &value, const char *output);
|
||||
|
||||
void printf(const char *output);
|
||||
void printf(const char *output, Register value);
|
||||
|
||||
|
@ -19,10 +19,38 @@
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
// Encodings:
|
||||
// [ptr] A fixed-size pointer.
|
||||
// [vwu] A variable-width unsigned integer.
|
||||
// [vws] A variable-width signed integer.
|
||||
// [u8] An 8-bit unsigned integer.
|
||||
// [u8'] An 8-bit unsigned integer which is potentially extended with packed
|
||||
// data.
|
||||
// [u8"] Packed data which is stored and packed in the previous [u8'].
|
||||
// [vwu*] A list of variable-width unsigned integers.
|
||||
// [pld] Payload of Recover Value Allocation:
|
||||
// PAYLOAD_NONE:
|
||||
// There is no payload.
|
||||
//
|
||||
// PAYLOAD_INDEX:
|
||||
// [vwu] Index, such as the constant pool index.
|
||||
//
|
||||
// PAYLOAD_STACK_OFFSET:
|
||||
// [vws] Stack offset based on the base of the Ion frame.
|
||||
//
|
||||
// PAYLOAD_GPR:
|
||||
// [u8] Code of the general register.
|
||||
//
|
||||
// PAYLOAD_FPU:
|
||||
// [u8] Code of the FPU register.
|
||||
//
|
||||
// PAYLOAD_PACKED_TAG:
|
||||
// [u8"] Bits 5-7: JSValueType is encoded on the low bits of the Mode
|
||||
// of the RValueAllocation.
|
||||
//
|
||||
// Snapshot header:
|
||||
//
|
||||
// [vwu] bits ((n+1)-31]: frame count
|
||||
// bit n+1: resume after
|
||||
// [vwu] bits ((n+1)-31]: recover instruction offset
|
||||
// bits [0,n): bailout kind (n = SNAPSHOT_BAILOUTKIND_BITS)
|
||||
//
|
||||
// Snapshot body, repeated "frame count" times, from oldest frame to newest frame.
|
||||
@ -92,35 +120,6 @@ using namespace js::jit;
|
||||
// Value with statically known type, which payload is stored at an
|
||||
// offset on the stack.
|
||||
//
|
||||
// Encodings:
|
||||
// [ptr] A fixed-size pointer.
|
||||
// [vwu] A variable-width unsigned integer.
|
||||
// [vws] A variable-width signed integer.
|
||||
// [u8] An 8-bit unsigned integer.
|
||||
// [u8'] An 8-bit unsigned integer which is potentially extended with packed
|
||||
// data.
|
||||
// [u8"] Packed data which is stored and packed in the previous [u8'].
|
||||
// [vwu*] A list of variable-width unsigned integers.
|
||||
// [pld] Payload of Recover Value Allocation:
|
||||
// PAYLOAD_NONE:
|
||||
// There is no payload.
|
||||
//
|
||||
// PAYLOAD_INDEX:
|
||||
// [vwu] Index, such as the constant pool index.
|
||||
//
|
||||
// PAYLOAD_STACK_OFFSET:
|
||||
// [vws] Stack offset based on the base of the Ion frame.
|
||||
//
|
||||
// PAYLOAD_GPR:
|
||||
// [u8] Code of the general register.
|
||||
//
|
||||
// PAYLOAD_FPU:
|
||||
// [u8] Code of the FPU register.
|
||||
//
|
||||
// PAYLOAD_PACKED_TAG:
|
||||
// [u8"] Bits 5-7: JSValueType is encoded on the low bits of the Mode
|
||||
// of the RValueAllocation.
|
||||
//
|
||||
|
||||
const RValueAllocation::Layout &
|
||||
RValueAllocation::layoutFromMode(Mode mode)
|
||||
@ -498,7 +497,7 @@ SnapshotReader::SnapshotReader(const uint8_t *snapshots, uint32_t offset,
|
||||
|
||||
// Details of snapshot header packing.
|
||||
static const uint32_t SNAPSHOT_BAILOUTKIND_SHIFT = 0;
|
||||
static const uint32_t SNAPSHOT_BAILOUTKIND_BITS = 5;
|
||||
static const uint32_t SNAPSHOT_BAILOUTKIND_BITS = 6;
|
||||
static const uint32_t SNAPSHOT_BAILOUTKIND_MASK = COMPUTE_MASK_(SNAPSHOT_BAILOUTKIND);
|
||||
|
||||
static const uint32_t SNAPSHOT_ROFFSET_SHIFT = COMPUTE_SHIFT_AFTER_(SNAPSHOT_BAILOUTKIND);
|
||||
|
@ -75,7 +75,7 @@ class StackSlotAllocator
|
||||
StackSlotAllocator() : height_(0)
|
||||
{ }
|
||||
|
||||
void freeSlot(LDefinition::Type type, uint32_t index) {
|
||||
static uint32_t width(LDefinition::Type type) {
|
||||
switch (type) {
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
case LDefinition::GENERAL:
|
||||
@ -83,7 +83,7 @@ class StackSlotAllocator
|
||||
case LDefinition::SLOTS:
|
||||
#endif
|
||||
case LDefinition::INT32:
|
||||
case LDefinition::FLOAT32: return freeSlot(index);
|
||||
case LDefinition::FLOAT32: return 4;
|
||||
#if JS_BITS_PER_WORD == 64
|
||||
case LDefinition::GENERAL:
|
||||
case LDefinition::OBJECT:
|
||||
@ -96,39 +96,29 @@ class StackSlotAllocator
|
||||
case LDefinition::TYPE:
|
||||
case LDefinition::PAYLOAD:
|
||||
#endif
|
||||
case LDefinition::DOUBLE: return freeDoubleSlot(index);
|
||||
case LDefinition::DOUBLE: return 8;
|
||||
case LDefinition::FLOAT32X4:
|
||||
case LDefinition::INT32X4: return freeQuadSlot(index);
|
||||
case LDefinition::INT32X4: return 16;
|
||||
}
|
||||
MOZ_CRASH("Unknown slot type");
|
||||
}
|
||||
|
||||
uint32_t allocateSlot(LDefinition::Type type) {
|
||||
switch (type) {
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
case LDefinition::GENERAL:
|
||||
case LDefinition::OBJECT:
|
||||
case LDefinition::SLOTS:
|
||||
#endif
|
||||
case LDefinition::INT32:
|
||||
case LDefinition::FLOAT32: return allocateSlot();
|
||||
#if JS_BITS_PER_WORD == 64
|
||||
case LDefinition::GENERAL:
|
||||
case LDefinition::OBJECT:
|
||||
case LDefinition::SLOTS:
|
||||
#endif
|
||||
#ifdef JS_PUNBOX64
|
||||
case LDefinition::BOX:
|
||||
#endif
|
||||
#ifdef JS_NUNBOX32
|
||||
case LDefinition::TYPE:
|
||||
case LDefinition::PAYLOAD:
|
||||
#endif
|
||||
case LDefinition::DOUBLE: return allocateDoubleSlot();
|
||||
case LDefinition::FLOAT32X4:
|
||||
case LDefinition::INT32X4: return allocateQuadSlot();
|
||||
void freeSlot(LDefinition::Type type, uint32_t index) {
|
||||
switch (width(type)) {
|
||||
case 4: return freeSlot(index);
|
||||
case 8: return freeDoubleSlot(index);
|
||||
case 16: return freeQuadSlot(index);
|
||||
}
|
||||
MOZ_CRASH("Unknown slot type");
|
||||
MOZ_CRASH("Unknown slot width");
|
||||
}
|
||||
|
||||
uint32_t allocateSlot(LDefinition::Type type) {
|
||||
switch (width(type)) {
|
||||
case 4: return allocateSlot();
|
||||
case 8: return allocateDoubleSlot();
|
||||
case 16: return allocateQuadSlot();
|
||||
}
|
||||
MOZ_CRASH("Unknown slot width");
|
||||
}
|
||||
|
||||
uint32_t stackHeight() const {
|
||||
|
@ -717,6 +717,29 @@ template bool ObjectPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruc
|
||||
template bool ObjectPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
|
||||
template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
|
||||
|
||||
template <unsigned Op>
|
||||
bool
|
||||
SimdSameAsReturnedTypePolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
|
||||
{
|
||||
MIRType type = ins->type();
|
||||
MOZ_ASSERT(IsSimdType(type));
|
||||
|
||||
MDefinition *in = ins->getOperand(Op);
|
||||
if (in->type() == type)
|
||||
return true;
|
||||
|
||||
MSimdUnbox *replace = MSimdUnbox::New(alloc, in, type);
|
||||
ins->block()->insertBefore(ins, replace);
|
||||
ins->replaceOperand(Op, replace);
|
||||
|
||||
return replace->typePolicy()->adjustInputs(alloc, replace);
|
||||
}
|
||||
|
||||
template bool
|
||||
SimdSameAsReturnedTypePolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
|
||||
template bool
|
||||
SimdSameAsReturnedTypePolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
|
||||
|
||||
bool
|
||||
CallPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
|
||||
{
|
||||
@ -1042,6 +1065,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
|
||||
_(MixPolicy<ObjectPolicy<0>, StringPolicy<1> >) \
|
||||
_(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<2> >) \
|
||||
_(MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >) \
|
||||
_(MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >) \
|
||||
_(MixPolicy<StringPolicy<0>, IntPolicy<1> >) \
|
||||
_(MixPolicy<StringPolicy<0>, StringPolicy<1> >) \
|
||||
_(NoFloatPolicy<0>) \
|
||||
|
@ -317,6 +317,19 @@ class SimdScalarPolicy MOZ_FINAL : public TypePolicy
|
||||
}
|
||||
};
|
||||
|
||||
// SIMD value-type policy, use the returned type of the instruction to determine
|
||||
// how to unbox its operand.
|
||||
template <unsigned Op>
|
||||
class SimdSameAsReturnedTypePolicy MOZ_FINAL : public TypePolicy
|
||||
{
|
||||
public:
|
||||
EMPTY_DATA_;
|
||||
static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
|
||||
virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE {
|
||||
return staticAdjustInputs(alloc, ins);
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned Op>
|
||||
class BoxPolicy MOZ_FINAL : public TypePolicy
|
||||
{
|
||||
|
@ -777,7 +777,7 @@ DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
|
||||
|
||||
JitFrameLayout *prefix = frame->framePrefix();
|
||||
EnsureExitFrame(prefix);
|
||||
cx->mainThread().jitTop = (uint8_t *)prefix;
|
||||
cx->runtime()->jitTop = (uint8_t *)prefix;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ class MacroAssemblerARM : public Assembler
|
||||
// address.
|
||||
Register secondScratchReg_;
|
||||
|
||||
public:
|
||||
// Higher level tag testing code.
|
||||
Operand ToPayload(Operand base) {
|
||||
return Operand(Register::FromCode(base.base()), base.disp());
|
||||
@ -45,6 +46,8 @@ class MacroAssemblerARM : public Assembler
|
||||
Address ToPayload(Address base) {
|
||||
return ToPayload(Operand(base)).toAddress();
|
||||
}
|
||||
|
||||
protected:
|
||||
Operand ToType(Operand base) {
|
||||
return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void *));
|
||||
}
|
||||
|
@ -4226,7 +4226,7 @@ Simulator::execute()
|
||||
int32_t rpc = resume_pc_;
|
||||
if (MOZ_UNLIKELY(rpc != 0)) {
|
||||
// AsmJS signal handler ran and we have to adjust the pc.
|
||||
PerThreadData::innermostAsmJSActivation()->setResumePC((void *)get_pc());
|
||||
JSRuntime::innermostAsmJSActivation()->setResumePC((void *)get_pc());
|
||||
set_pc(rpc);
|
||||
resume_pc_ = 0;
|
||||
}
|
||||
@ -4418,16 +4418,34 @@ Simulator::Current()
|
||||
} // namespace js
|
||||
|
||||
js::jit::Simulator *
|
||||
js::PerThreadData::simulator() const
|
||||
JSRuntime::simulator() const
|
||||
{
|
||||
return simulator_;
|
||||
}
|
||||
|
||||
js::jit::Simulator *
|
||||
js::PerThreadData::simulator() const
|
||||
{
|
||||
return runtime_->simulator();
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::setSimulator(js::jit::Simulator *sim)
|
||||
{
|
||||
simulator_ = sim;
|
||||
simulatorStackLimit_ = sim->stackLimit();
|
||||
}
|
||||
|
||||
void
|
||||
js::PerThreadData::setSimulator(js::jit::Simulator *sim)
|
||||
{
|
||||
simulator_ = sim;
|
||||
simulatorStackLimit_ = sim->stackLimit();
|
||||
runtime_->setSimulator(sim);
|
||||
}
|
||||
|
||||
uintptr_t *
|
||||
JSRuntime::addressOfSimulatorStackLimit()
|
||||
{
|
||||
return &simulatorStackLimit_;
|
||||
}
|
||||
|
||||
js::jit::SimulatorRuntime *
|
||||
@ -4436,12 +4454,6 @@ js::PerThreadData::simulatorRuntime() const
|
||||
return runtime_->simulatorRuntime();
|
||||
}
|
||||
|
||||
uintptr_t *
|
||||
js::PerThreadData::addressOfSimulatorStackLimit()
|
||||
{
|
||||
return &simulatorStackLimit_;
|
||||
}
|
||||
|
||||
js::jit::SimulatorRuntime *
|
||||
JSRuntime::simulatorRuntime() const
|
||||
{
|
||||
|
@ -377,7 +377,7 @@ class Simulator
|
||||
|
||||
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (cx->mainThread().simulator()->overRecursedWithExtra(extra)) { \
|
||||
if (cx->runtime()->simulator()->overRecursedWithExtra(extra)) { \
|
||||
js_ReportOverRecursed(cx); \
|
||||
onerror; \
|
||||
} \
|
||||
|
@ -67,12 +67,13 @@ static_assert(1 << defaultShift == sizeof(jsval), "The defaultShift is wrong");
|
||||
|
||||
class MacroAssemblerMIPS : public Assembler
|
||||
{
|
||||
protected:
|
||||
public:
|
||||
// higher level tag testing code
|
||||
Operand ToPayload(Operand base);
|
||||
Address ToPayload(Address base) {
|
||||
return ToPayload(Operand(base)).toAddress();
|
||||
}
|
||||
protected:
|
||||
Operand ToType(Operand base);
|
||||
Address ToType(Address base) {
|
||||
return ToType(Operand(base)).toAddress();
|
||||
|
@ -3312,7 +3312,7 @@ Simulator::execute()
|
||||
// Get the PC to simulate. Cannot use the accessor here as we need the
|
||||
// raw PC value and not the one used as input to arithmetic instructions.
|
||||
int program_counter = get_pc();
|
||||
AsmJSActivation *activation = TlsPerThreadData.get()->asmJSActivationStack();
|
||||
AsmJSActivation *activation = TlsPerThreadData.get()->runtimeFromMainThread()->asmJSActivationStack();
|
||||
|
||||
while (program_counter != end_sim_pc) {
|
||||
if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) {
|
||||
@ -3470,16 +3470,34 @@ Simulator::popAddress()
|
||||
} // namespace js
|
||||
|
||||
js::jit::Simulator *
|
||||
js::PerThreadData::simulator() const
|
||||
JSRuntime::simulator() const
|
||||
{
|
||||
return simulator_;
|
||||
}
|
||||
|
||||
js::jit::Simulator *
|
||||
js::PerThreadData::simulator() const
|
||||
{
|
||||
return runtime_->simulator();
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::setSimulator(js::jit::Simulator *sim)
|
||||
{
|
||||
simulator_ = sim;
|
||||
simulatorStackLimit_ = sim->stackLimit();
|
||||
}
|
||||
|
||||
void
|
||||
js::PerThreadData::setSimulator(js::jit::Simulator *sim)
|
||||
{
|
||||
simulator_ = sim;
|
||||
simulatorStackLimit_ = sim->stackLimit();
|
||||
runtime_->setSimulator(sim);
|
||||
}
|
||||
|
||||
uintptr_t *
|
||||
JSRuntime::addressOfSimulatorStackLimit()
|
||||
{
|
||||
return &simulatorStackLimit_;
|
||||
}
|
||||
|
||||
js::jit::SimulatorRuntime *
|
||||
@ -3488,12 +3506,6 @@ js::PerThreadData::simulatorRuntime() const
|
||||
return runtime_->simulatorRuntime();
|
||||
}
|
||||
|
||||
uintptr_t *
|
||||
js::PerThreadData::addressOfSimulatorStackLimit()
|
||||
{
|
||||
return &simulatorStackLimit_;
|
||||
}
|
||||
|
||||
js::jit::SimulatorRuntime *
|
||||
JSRuntime::simulatorRuntime() const
|
||||
{
|
||||
|
@ -161,6 +161,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// X86/X64-common interface.
|
||||
/////////////////////////////////////////////////////////////////
|
||||
Address ToPayload(Address value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
void storeValue(ValueOperand val, Operand dest) {
|
||||
movq(val.valueReg(), dest);
|
||||
}
|
||||
|
@ -380,18 +380,47 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut)
|
||||
// Do not erase the frame pointer in this function.
|
||||
|
||||
MacroAssembler masm(cx);
|
||||
// Caller:
|
||||
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- rsp
|
||||
// '--- #r8 ---'
|
||||
|
||||
// ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
|
||||
// Including |this|, there are (|nargs| + 1) arguments to copy.
|
||||
MOZ_ASSERT(ArgumentsRectifierReg == r8);
|
||||
|
||||
// Load the number of |undefined|s to push into %rcx.
|
||||
// Add |this|, in the counter of known arguments.
|
||||
masm.addl(Imm32(1), r8);
|
||||
|
||||
// Load |nformals| into %rcx.
|
||||
masm.loadPtr(Address(rsp, RectifierFrameLayout::offsetOfCalleeToken()), rax);
|
||||
masm.mov(rax, rcx);
|
||||
masm.andq(Imm32(uint32_t(CalleeTokenMask)), rcx);
|
||||
masm.movzwl(Operand(rcx, JSFunction::offsetOfNargs()), rcx);
|
||||
|
||||
// Including |this|, there are (|nformals| + 1) arguments to push to the
|
||||
// stack. Then we push a JitFrameLayout. We compute the padding expressed
|
||||
// in the number of extra |undefined| values to push on the stack.
|
||||
static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
|
||||
"No need to consider the JitFrameLayout for aligning the stack");
|
||||
static_assert(JitStackAlignment % sizeof(Value) == 0,
|
||||
"Ensure that we can pad the stack by pushing extra UndefinedValue");
|
||||
|
||||
const uint32_t alignment = JitStackAlignment / sizeof(Value);
|
||||
MOZ_ASSERT(IsPowerOfTwo(alignment));
|
||||
masm.addl(Imm32(alignment - 1 /* for padding */ + 1 /* for |this| */), rcx);
|
||||
masm.andl(Imm32(~(alignment - 1)), rcx);
|
||||
|
||||
// Load the number of |undefined|s to push into %rcx.
|
||||
masm.subq(r8, rcx);
|
||||
|
||||
// Caller:
|
||||
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- rsp <- r9
|
||||
// '------ #r8 -------'
|
||||
//
|
||||
// Rectifier frame:
|
||||
// [undef] [undef] [undef] [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]]
|
||||
// '------- #rcx --------' '------ #r8 -------'
|
||||
|
||||
// Copy the number of actual arguments
|
||||
masm.loadPtr(Address(rsp, RectifierFrameLayout::offsetOfNumActualArgs()), rdx);
|
||||
|
||||
@ -399,7 +428,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut)
|
||||
|
||||
masm.movq(rsp, r9); // Save %rsp.
|
||||
|
||||
// Push undefined.
|
||||
// Push undefined. (including the padding)
|
||||
{
|
||||
Label undefLoopTop;
|
||||
masm.bind(&undefLoopTop);
|
||||
@ -410,11 +439,14 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut)
|
||||
}
|
||||
|
||||
// Get the topmost argument.
|
||||
BaseIndex b = BaseIndex(r9, r8, TimesEight, sizeof(RectifierFrameLayout));
|
||||
static_assert(sizeof(Value) == 8, "TimesEight is used to skip arguments");
|
||||
|
||||
// | - sizeof(Value)| is used to put rcx such that we can read the last
|
||||
// argument, and not the value which is after.
|
||||
BaseIndex b = BaseIndex(r9, r8, TimesEight, sizeof(RectifierFrameLayout) - sizeof(Value));
|
||||
masm.lea(Operand(b), rcx);
|
||||
|
||||
// Push arguments, |nargs| + 1 times (to include |this|).
|
||||
masm.addl(Imm32(1), r8);
|
||||
// Copy & Push arguments, |nargs| + 1 times (to include |this|).
|
||||
{
|
||||
Label copyLoopTop;
|
||||
|
||||
@ -425,6 +457,14 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut)
|
||||
masm.j(Assembler::NonZero, ©LoopTop);
|
||||
}
|
||||
|
||||
// Caller:
|
||||
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- r9
|
||||
//
|
||||
//
|
||||
// Rectifier frame:
|
||||
// [undef] [undef] [undef] [arg2] [arg1] [this] <- rsp [[argc] [callee] [descr] [raddr]]
|
||||
//
|
||||
|
||||
// Construct descriptor.
|
||||
masm.subq(rsp, r9);
|
||||
masm.makeFrameDescriptor(r9, JitFrame_Rectifier);
|
||||
|
@ -370,6 +370,9 @@ JitCode *
|
||||
JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
// Caller:
|
||||
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- esp
|
||||
// '-- #esi ---'
|
||||
|
||||
// ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
|
||||
// Including |this|, there are (|nargs| + 1) arguments to copy.
|
||||
@ -380,6 +383,22 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut)
|
||||
masm.mov(eax, ecx);
|
||||
masm.andl(Imm32(CalleeTokenMask), ecx);
|
||||
masm.movzwl(Operand(ecx, JSFunction::offsetOfNargs()), ecx);
|
||||
|
||||
// The frame pointer and its padding are pushed on the stack.
|
||||
// Including |this|, there are (|nformals| + 1) arguments to push to the
|
||||
// stack. Then we push a JitFrameLayout. We compute the padding expressed
|
||||
// in the number of extra |undefined| values to push on the stack.
|
||||
static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
|
||||
"No need to consider the JitFrameLayout for aligning the stack");
|
||||
static_assert((sizeof(Value) + 2 * sizeof(void *)) % JitStackAlignment == 0,
|
||||
"No need to consider |this| and the frame pointer and its padding for aligning the stack");
|
||||
static_assert(JitStackAlignment % sizeof(Value) == 0,
|
||||
"Ensure that we can pad the stack by pushing extra UndefinedValue");
|
||||
|
||||
const uint32_t alignment = JitStackAlignment / sizeof(Value);
|
||||
MOZ_ASSERT(IsPowerOfTwo(alignment));
|
||||
masm.addl(Imm32(alignment - 1 /* for padding */), ecx);
|
||||
masm.andl(Imm32(~(alignment - 1)), ecx);
|
||||
masm.subl(esi, ecx);
|
||||
|
||||
// Copy the number of actual arguments.
|
||||
@ -393,6 +412,17 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, void **returnAddrOut)
|
||||
// BaselineJIT.cpp/InitFromBailout. Check for the |#if defined(JS_CODEGEN_X86)| portions.
|
||||
masm.push(FramePointer);
|
||||
masm.movl(esp, FramePointer); // Save %esp.
|
||||
masm.push(FramePointer /* padding */);
|
||||
|
||||
// Caller:
|
||||
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]]
|
||||
// '-- #esi ---'
|
||||
//
|
||||
// Rectifier frame:
|
||||
// [ebp'] <- ebp [padding] <- esp [undef] [undef] [arg2] [arg1] [this]
|
||||
// '--- #ecx ----' '-- #esi ---'
|
||||
//
|
||||
// [[argc] [callee] [descr] [raddr]]
|
||||
|
||||
// Push undefined.
|
||||
{
|
||||
|
@ -1732,7 +1732,7 @@ JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, size_t trusted
|
||||
SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize);
|
||||
SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize);
|
||||
|
||||
rt->mainThread.initJitStackLimit();
|
||||
rt->initJitStackLimit();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
@ -5894,7 +5894,7 @@ HideScriptedCaller(JSContext *cx)
|
||||
|
||||
// If there's no accessible activation on the stack, we'll return null from
|
||||
// DescribeScriptedCaller anyway, so there's no need to annotate anything.
|
||||
Activation *act = cx->runtime()->mainThread.activation();
|
||||
Activation *act = cx->runtime()->activation();
|
||||
if (!act)
|
||||
return;
|
||||
act->hideScriptedCaller();
|
||||
@ -5903,7 +5903,7 @@ HideScriptedCaller(JSContext *cx)
|
||||
JS_PUBLIC_API(void)
|
||||
UnhideScriptedCaller(JSContext *cx)
|
||||
{
|
||||
Activation *act = cx->runtime()->mainThread.activation();
|
||||
Activation *act = cx->runtime()->activation();
|
||||
if (!act)
|
||||
return;
|
||||
act->unhideScriptedCaller();
|
||||
|
@ -1047,7 +1047,7 @@ JSContext::saveFrameChain()
|
||||
if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_)))
|
||||
return false;
|
||||
|
||||
if (Activation *act = mainThread().activation())
|
||||
if (Activation *act = runtime()->activation())
|
||||
act->saveFrameChain();
|
||||
|
||||
setCompartment(nullptr);
|
||||
@ -1065,7 +1065,7 @@ JSContext::restoreFrameChain()
|
||||
setCompartment(sfc.compartment);
|
||||
enterCompartmentDepth_ = sfc.enterCompartmentCount;
|
||||
|
||||
if (Activation *act = mainThread().activation())
|
||||
if (Activation *act = runtime()->activation())
|
||||
act->restoreFrameChain();
|
||||
}
|
||||
|
||||
@ -1190,7 +1190,7 @@ void *
|
||||
ExclusiveContext::stackLimitAddressForJitCode(StackKind kind)
|
||||
{
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
return runtime_->mainThread.addressOfSimulatorStackLimit();
|
||||
return runtime_->addressOfSimulatorStackLimit();
|
||||
#endif
|
||||
return stackLimitAddress(kind);
|
||||
}
|
||||
|
@ -241,6 +241,7 @@ class ExclusiveContext : public ContextFriendFields,
|
||||
void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
|
||||
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
||||
void *stackLimitAddressForJitCode(StackKind kind);
|
||||
uintptr_t stackLimit(StackKind kind) { return runtime_->mainThread.nativeStackLimit[kind]; }
|
||||
size_t gcSystemPageSize() { return gc::SystemPageSize(); }
|
||||
bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
|
||||
bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
|
||||
@ -435,16 +436,16 @@ struct JSContext : public js::ExclusiveContext,
|
||||
bool currentlyRunning() const;
|
||||
|
||||
bool currentlyRunningInInterpreter() const {
|
||||
return mainThread().activation()->isInterpreter();
|
||||
return runtime_->activation()->isInterpreter();
|
||||
}
|
||||
bool currentlyRunningInJit() const {
|
||||
return mainThread().activation()->isJit();
|
||||
return runtime_->activation()->isJit();
|
||||
}
|
||||
js::InterpreterFrame *interpreterFrame() const {
|
||||
return mainThread().activation()->asInterpreter()->current();
|
||||
return runtime_->activation()->asInterpreter()->current();
|
||||
}
|
||||
js::InterpreterRegs &interpreterRegs() const {
|
||||
return mainThread().activation()->asInterpreter()->regs();
|
||||
return runtime_->activation()->asInterpreter()->regs();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -448,7 +448,7 @@ JSContext::currentScript(jsbytecode **ppc,
|
||||
if (ppc)
|
||||
*ppc = nullptr;
|
||||
|
||||
js::Activation *act = mainThread().activation();
|
||||
js::Activation *act = runtime()->activation();
|
||||
while (act && (act->cx() != this || (act->isJit() && !act->asJit()->isActive())))
|
||||
act = act->prev();
|
||||
|
||||
|
@ -2285,6 +2285,7 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
|
||||
allocationSiteTable = cx->new_<AllocationSiteTable>();
|
||||
if (!allocationSiteTable || !allocationSiteTable->init()) {
|
||||
js_delete(allocationSiteTable);
|
||||
allocationSiteTable = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -3330,7 +3331,8 @@ TypeObject::clearNewScript(ExclusiveContext *cx)
|
||||
newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this);
|
||||
} else {
|
||||
// Threads with an ExclusiveContext are not allowed to run scripts.
|
||||
MOZ_ASSERT(!cx->perThreadData->activation());
|
||||
MOZ_ASSERT(!cx->perThreadData->runtimeIfOnOwnerThread() ||
|
||||
!cx->perThreadData->runtimeIfOnOwnerThread()->activation());
|
||||
}
|
||||
|
||||
js_delete(newScript);
|
||||
@ -4611,7 +4613,9 @@ ConstraintTypeSet::sweep(Zone *zone, AutoClearTypeInferenceStateOnOOM &oom)
|
||||
objectCount = 0;
|
||||
for (unsigned i = 0; i < oldCapacity; i++) {
|
||||
TypeObjectKey *object = oldArray[i];
|
||||
if (object && !IsAboutToBeFinalized(&object)) {
|
||||
if (!object)
|
||||
continue;
|
||||
if (!IsAboutToBeFinalized(&object)) {
|
||||
TypeObjectKey **pentry =
|
||||
HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
|
||||
(zone->types.typeLifoAlloc, objectSet, objectCount, object);
|
||||
@ -4624,16 +4628,28 @@ ConstraintTypeSet::sweep(Zone *zone, AutoClearTypeInferenceStateOnOOM &oom)
|
||||
objectCount = 0;
|
||||
break;
|
||||
}
|
||||
} else if (object->isTypeObject() && object->asTypeObject()->unknownProperties()) {
|
||||
// Object sets containing objects with unknown properties might
|
||||
// not be complete. Mark the type set as unknown, which it will
|
||||
// be treated as during Ion compilation.
|
||||
flags |= TYPE_FLAG_ANYOBJECT;
|
||||
clearObjects();
|
||||
objectCount = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setBaseObjectCount(objectCount);
|
||||
} else if (objectCount == 1) {
|
||||
TypeObjectKey *object = (TypeObjectKey *) objectSet;
|
||||
if (IsAboutToBeFinalized(&object)) {
|
||||
if (!IsAboutToBeFinalized(&object)) {
|
||||
objectSet = reinterpret_cast<TypeObjectKey **>(object);
|
||||
} else {
|
||||
// As above, mark type sets containing objects with unknown
|
||||
// properties as unknown.
|
||||
if (object->isTypeObject() && object->asTypeObject()->unknownProperties())
|
||||
flags |= TYPE_FLAG_ANYOBJECT;
|
||||
objectSet = nullptr;
|
||||
setBaseObjectCount(0);
|
||||
} else {
|
||||
objectSet = reinterpret_cast<TypeObjectKey **>(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,7 +454,7 @@ struct PerThreadDataFriendFields
|
||||
}
|
||||
|
||||
/* Limit pointer for checking native stack consumption. */
|
||||
uintptr_t nativeStackLimit[StackKindCount];
|
||||
uintptr_t nativeStackLimit[js::StackKindCount];
|
||||
|
||||
static const size_t RuntimeMainThreadOffset = offsetof(RuntimeDummy, mainThread);
|
||||
|
||||
|
@ -4209,7 +4209,7 @@ EnableSingleStepProfiling(JSContext *cx, unsigned argc, Value *vp)
|
||||
#if defined(JS_ARM_SIMULATOR)
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
jit::Simulator *sim = cx->runtime()->mainThread.simulator();
|
||||
jit::Simulator *sim = cx->runtime()->simulator();
|
||||
sim->enable_single_stepping(SingleStepCallback, cx->runtime());
|
||||
|
||||
args.rval().setUndefined();
|
||||
@ -4226,7 +4226,7 @@ DisableSingleStepProfiling(JSContext *cx, unsigned argc, Value *vp)
|
||||
#if defined(JS_ARM_SIMULATOR)
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
jit::Simulator *sim = cx->runtime()->mainThread.simulator();
|
||||
jit::Simulator *sim = cx->runtime()->simulator();
|
||||
sim->disable_single_stepping();
|
||||
|
||||
AutoValueVector elems(cx);
|
||||
|
@ -1084,6 +1084,42 @@ Debugger::receiveCompletionValue(Maybe<AutoCompartment> &ac, bool ok,
|
||||
return newCompletionValue(cx, status, value, vp);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetStatusProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, JSTrapStatus status,
|
||||
JSTrapStatus *statusOut, MutableHandleValue vp, int *hits)
|
||||
{
|
||||
bool found;
|
||||
if (!HasProperty(cx, obj, name, &found))
|
||||
return false;
|
||||
if (found) {
|
||||
++*hits;
|
||||
*statusOut = status;
|
||||
if (!GetProperty(cx, obj, obj, name, vp))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseResumptionValueAsObject(JSContext *cx, HandleValue rv, JSTrapStatus *statusp,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
int hits = 0;
|
||||
if (rv.isObject()) {
|
||||
RootedObject obj(cx, &rv.toObject());
|
||||
if (!GetStatusProperty(cx, obj, cx->names().return_, JSTRAP_RETURN, statusp, vp, &hits))
|
||||
return false;
|
||||
if (!GetStatusProperty(cx, obj, cx->names().throw_, JSTRAP_THROW, statusp, vp, &hits))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hits != 1) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSTrapStatus
|
||||
Debugger::parseResumptionValue(Maybe<AutoCompartment> &ac, bool ok, const Value &rv, MutableHandleValue vp,
|
||||
bool callHook)
|
||||
@ -1100,35 +1136,16 @@ Debugger::parseResumptionValue(Maybe<AutoCompartment> &ac, bool ok, const Value
|
||||
return JSTRAP_ERROR;
|
||||
}
|
||||
|
||||
/* Check that rv is {return: val} or {throw: val}. */
|
||||
JSContext *cx = ac->context()->asJSContext();
|
||||
Rooted<JSObject*> obj(cx);
|
||||
RootedShape shape(cx);
|
||||
RootedId returnId(cx, NameToId(cx->names().return_));
|
||||
RootedId throwId(cx, NameToId(cx->names().throw_));
|
||||
bool okResumption = rv.isObject();
|
||||
if (okResumption) {
|
||||
obj = &rv.toObject();
|
||||
okResumption = obj->is<PlainObject>();
|
||||
}
|
||||
if (okResumption) {
|
||||
shape = obj->lastProperty();
|
||||
okResumption = shape->previous() &&
|
||||
!shape->previous()->previous() &&
|
||||
(shape->propid() == returnId || shape->propid() == throwId) &&
|
||||
shape->isDataDescriptor();
|
||||
}
|
||||
if (!okResumption) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION);
|
||||
JSTrapStatus status = JSTRAP_CONTINUE;
|
||||
RootedValue v(cx);
|
||||
RootedValue rvRoot(cx, rv);
|
||||
if (!ParseResumptionValueAsObject(cx, rvRoot, &status, &v) ||
|
||||
!unwrapDebuggeeValue(cx, &v))
|
||||
{
|
||||
return handleUncaughtException(ac, vp, callHook);
|
||||
}
|
||||
|
||||
HandleNativeObject nobj = obj.as<NativeObject>();
|
||||
|
||||
RootedValue v(cx, vp.get());
|
||||
if (!NativeGetExistingProperty(cx, obj, nobj, shape, &v) || !unwrapDebuggeeValue(cx, &v))
|
||||
return handleUncaughtException(ac, &v, callHook);
|
||||
|
||||
ac.reset();
|
||||
if (!cx->compartment()->wrap(cx, &v)) {
|
||||
vp.setUndefined();
|
||||
@ -1136,7 +1153,7 @@ Debugger::parseResumptionValue(Maybe<AutoCompartment> &ac, bool ok, const Value
|
||||
}
|
||||
vp.set(v);
|
||||
|
||||
return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW;
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -1518,22 +1518,38 @@ js::NativeDefineElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t i
|
||||
return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs);
|
||||
}
|
||||
|
||||
|
||||
/*** [[Get]] *************************************************************************************/
|
||||
|
||||
static inline bool
|
||||
CallGetter(JSContext* cx, HandleObject receiver, HandleShape shape, MutableHandleValue vp)
|
||||
{
|
||||
MOZ_ASSERT(!shape->hasDefaultGetter());
|
||||
|
||||
if (shape->hasGetterValue()) {
|
||||
Value fval = shape->getterValue();
|
||||
return InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp);
|
||||
}
|
||||
|
||||
RootedId id(cx, shape->propid());
|
||||
return CallJSPropertyOp(cx, shape->getterOp(), receiver, id, vp);
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
NativeGetExistingPropertyInline(JSContext *cx,
|
||||
typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
|
||||
typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
|
||||
typename MaybeRooted<NativeObject*, allowGC>::HandleType pobj,
|
||||
typename MaybeRooted<Shape*, allowGC>::HandleType shape,
|
||||
typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
|
||||
GetExistingProperty(JSContext *cx,
|
||||
typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
|
||||
typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
|
||||
typename MaybeRooted<Shape*, allowGC>::HandleType shape,
|
||||
typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
|
||||
{
|
||||
if (shape->hasSlot()) {
|
||||
vp.set(pobj->getSlot(shape->slot()));
|
||||
vp.set(obj->getSlot(shape->slot()));
|
||||
MOZ_ASSERT_IF(!vp.isMagic(JS_UNINITIALIZED_LEXICAL) &&
|
||||
!pobj->hasSingletonType() &&
|
||||
!pobj->template is<ScopeObject>() &&
|
||||
!obj->hasSingletonType() &&
|
||||
!obj->template is<ScopeObject>() &&
|
||||
shape->hasDefaultGetter(),
|
||||
js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
|
||||
js::types::TypeHasProperty(cx, obj->type(), shape->propid(), vp));
|
||||
} else {
|
||||
vp.setUndefined();
|
||||
}
|
||||
@ -1559,40 +1575,41 @@ NativeGetExistingPropertyInline(JSContext *cx,
|
||||
if (!allowGC)
|
||||
return false;
|
||||
|
||||
if (!shape->get(cx,
|
||||
if (!CallGetter(cx,
|
||||
MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
|
||||
MaybeRooted<JSObject*, allowGC>::toHandle(obj),
|
||||
MaybeRooted<JSObject*, allowGC>::toHandle(pobj),
|
||||
MaybeRooted<Shape*, allowGC>::toHandle(shape),
|
||||
MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Update slotful shapes according to the value produced by the getter. */
|
||||
if (shape->hasSlot() && pobj->contains(cx, shape))
|
||||
pobj->setSlot(shape->slot(), vp);
|
||||
// Ancient nonstandard extension: via the JSAPI it's possible to create a
|
||||
// data property that has both a slot and a getter. In that case, copy the
|
||||
// value returned by the getter back into the slot.
|
||||
if (shape->hasSlot() && obj->contains(cx, shape))
|
||||
obj->setSlot(shape->slot(), vp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::NativeGetExistingProperty(JSContext *cx, HandleObject obj, HandleNativeObject pobj,
|
||||
js::NativeGetExistingProperty(JSContext *cx, HandleObject receiver, HandleNativeObject obj,
|
||||
HandleShape shape, MutableHandleValue vp)
|
||||
{
|
||||
return NativeGetExistingPropertyInline<CanGC>(cx, obj, obj, pobj, shape, vp);
|
||||
return GetExistingProperty<CanGC>(cx, receiver, obj, shape, vp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given pc pointing after a property accessing bytecode, return true if the
|
||||
* access is "object-detecting" in the sense used by web scripts, e.g., when
|
||||
* checking whether document.all is defined.
|
||||
* access is "property-detecting" -- that is, if we shouldn't warn about it
|
||||
* even if no such property is found and strict warnings are enabled.
|
||||
*/
|
||||
static bool
|
||||
Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
MOZ_ASSERT(script->containsPC(pc));
|
||||
|
||||
/* General case: a branch or equality op follows the access. */
|
||||
// General case: a branch or equality op follows the access.
|
||||
JSOp op = JSOp(*pc);
|
||||
if (js_CodeSpec[op].format & JOF_DETECTING)
|
||||
return true;
|
||||
@ -1600,10 +1617,7 @@ Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
jsbytecode *endpc = script->codeEnd();
|
||||
|
||||
if (op == JSOP_NULL) {
|
||||
/*
|
||||
* Special case #1: handle (document.all == null). Don't sweat
|
||||
* about JS1.2's revision of the equality operators here.
|
||||
*/
|
||||
// Special case #1: don't warn about (obj.prop == null).
|
||||
if (++pc < endpc) {
|
||||
op = JSOp(*pc);
|
||||
return op == JSOP_EQ || op == JSOP_NE;
|
||||
@ -1612,11 +1626,7 @@ Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
}
|
||||
|
||||
if (op == JSOP_GETGNAME || op == JSOP_GETNAME) {
|
||||
/*
|
||||
* Special case #2: handle (document.all == undefined). Don't worry
|
||||
* about a local variable named |undefined| shadowing the immutable
|
||||
* global binding...because, really?
|
||||
*/
|
||||
// Special case #2: don't warn about (obj.prop == undefined).
|
||||
JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
|
||||
if (atom == cx->names().undefined &&
|
||||
(pc += js_CodeSpec[op].length) < endpc) {
|
||||
@ -1628,149 +1638,185 @@ Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish getting the property `receiver[id]` after looking at every object on
|
||||
* the prototype chain and not finding any such property.
|
||||
*
|
||||
* Per the spec, this should just set the result to `undefined` and call it a
|
||||
* day. However:
|
||||
*
|
||||
* 1. We add support for the nonstandard JSClass::getProperty hook.
|
||||
*
|
||||
* 2. This function also runs when we're evaluating an expression that's an
|
||||
* Identifier (that is, an unqualified name lookup), so we need to figure
|
||||
* out if that's what's happening and throw a ReferenceError if so.
|
||||
*
|
||||
* 3. We also emit an optional warning for this. (It's not super useful on the
|
||||
* web, as there are too many false positives, but anecdotally useful in
|
||||
* Gecko code.)
|
||||
*/
|
||||
static bool
|
||||
GetNonexistentProperty(JSContext *cx, HandleNativeObject obj, HandleId id,
|
||||
HandleObject receiver, MutableHandleValue vp)
|
||||
{
|
||||
vp.setUndefined();
|
||||
|
||||
// Non-standard extension: Call the getProperty hook. If it sets vp to a
|
||||
// value other than undefined, we're done. If not, fall through to the
|
||||
// warning/error checks below.
|
||||
if (JSPropertyOp getProperty = obj->getClass()->getProperty) {
|
||||
if (!CallJSPropertyOp(cx, getProperty, obj, id, vp))
|
||||
return false;
|
||||
|
||||
if (!vp.isUndefined())
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are doing a name lookup, this is a ReferenceError.
|
||||
jsbytecode *pc = nullptr;
|
||||
RootedScript script(cx, cx->currentScript(&pc));
|
||||
if (!pc)
|
||||
return true;
|
||||
JSOp op = (JSOp) *pc;
|
||||
if (op == JSOP_GETXPROP) {
|
||||
JSAutoByteString printable;
|
||||
if (js_ValueToPrintable(cx, IdToValue(id), &printable))
|
||||
js_ReportIsNotDefined(cx, printable.ptr());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Give a strict warning if foo.bar is evaluated by a script for an object
|
||||
// foo with no property named 'bar'.
|
||||
//
|
||||
// Don't warn if extra warnings not enabled or for random getprop
|
||||
// operations.
|
||||
if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM))
|
||||
return true;
|
||||
|
||||
// Don't warn repeatedly for the same script.
|
||||
if (!script || script->warnedAboutUndefinedProp())
|
||||
return true;
|
||||
|
||||
// Don't warn in self-hosted code (where the further presence of
|
||||
// JS::RuntimeOptions::werror() would result in impossible-to-avoid
|
||||
// errors to entirely-innocent client code).
|
||||
if (script->selfHosted())
|
||||
return true;
|
||||
|
||||
// We may just be checking if that object has an iterator.
|
||||
if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
|
||||
return true;
|
||||
|
||||
// Do not warn about tests like (obj[prop] == undefined).
|
||||
pc += js_CodeSpec[op].length;
|
||||
if (Detecting(cx, script, pc))
|
||||
return true;
|
||||
|
||||
unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
|
||||
script->setWarnedAboutUndefinedProp();
|
||||
|
||||
// Ok, bad undefined property reference: whine about it.
|
||||
RootedValue val(cx, IdToValue(id));
|
||||
return js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, val,
|
||||
js::NullPtr(), nullptr, nullptr);
|
||||
}
|
||||
|
||||
/* The NoGC version of GetNonexistentProperty, present only to make types line up. */
|
||||
bool
|
||||
GetNonexistentProperty(JSContext *cx, NativeObject *obj, jsid id, JSObject *receiver,
|
||||
FakeMutableHandle<Value> vp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
GeneralizedGetProperty(JSContext *cx, HandleObject obj, HandleId id, HandleObject receiver,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return GetProperty(cx, obj, receiver, id, vp);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
GeneralizedGetProperty(JSContext *cx, JSObject *obj, jsid id, JSObject *receiver,
|
||||
FakeMutableHandle<Value> vp)
|
||||
{
|
||||
JS_CHECK_RECURSION_DONT_REPORT(cx, return false);
|
||||
return GetPropertyNoGC(cx, obj, receiver, id, vp.address());
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
GetPropertyHelperInline(JSContext *cx,
|
||||
NativeGetPropertyInline(JSContext *cx,
|
||||
typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
|
||||
typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
|
||||
typename MaybeRooted<jsid, allowGC>::HandleType id,
|
||||
typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
|
||||
{
|
||||
/* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */
|
||||
typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx);
|
||||
typename MaybeRooted<NativeObject*, allowGC>::RootType pobj(cx, obj);
|
||||
typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
|
||||
if (!LookupPropertyInline<allowGC>(cx, obj, id, &obj2, &shape))
|
||||
return false;
|
||||
|
||||
if (!shape) {
|
||||
if (!allowGC)
|
||||
// This loop isn't explicit in the spec algorithm. See the comment on step
|
||||
// 4.d below.
|
||||
for (;;) {
|
||||
// Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
|
||||
bool done;
|
||||
if (!LookupOwnPropertyInline<allowGC>(cx, pobj, id, &shape, &done))
|
||||
return false;
|
||||
|
||||
vp.setUndefined();
|
||||
|
||||
if (JSPropertyOp getProperty = obj->getClass()->getProperty) {
|
||||
if (!CallJSPropertyOp(cx, getProperty,
|
||||
MaybeRooted<JSObject*, allowGC>::toHandle(obj),
|
||||
MaybeRooted<jsid, allowGC>::toHandle(id),
|
||||
MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
|
||||
{
|
||||
return false;
|
||||
if (shape) {
|
||||
// Steps 5-8. Special case for dense elements because
|
||||
// GetExistingProperty doesn't support those.
|
||||
if (IsImplicitDenseOrTypedArrayElement(shape)) {
|
||||
vp.set(pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
|
||||
return true;
|
||||
}
|
||||
return GetExistingProperty<allowGC>(cx, receiver, pobj, shape, vp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give a strict warning if foo.bar is evaluated by a script for an
|
||||
* object foo with no property named 'bar'.
|
||||
*/
|
||||
if (vp.isUndefined()) {
|
||||
jsbytecode *pc = nullptr;
|
||||
RootedScript script(cx, cx->currentScript(&pc));
|
||||
if (!pc)
|
||||
return true;
|
||||
JSOp op = (JSOp) *pc;
|
||||
// Steps 4.a-b. The check for 'done' on this next line is tricky.
|
||||
// done can be true in exactly these unlikely-sounding cases:
|
||||
// - We're looking up an element, and pobj is a TypedArray that
|
||||
// doesn't have that many elements.
|
||||
// - We're being called from a resolve hook to assign to the property
|
||||
// being resolved.
|
||||
// What they all have in common is we do not want to keep walking
|
||||
// the prototype chain.
|
||||
RootedObject proto(cx, done ? nullptr : pobj->getProto());
|
||||
|
||||
if (op == JSOP_GETXPROP) {
|
||||
/* Undefined property during a name lookup, report an error. */
|
||||
JSAutoByteString printable;
|
||||
if (js_ValueToPrintable(cx, IdToValue(id), &printable))
|
||||
js_ReportIsNotDefined(cx, printable.ptr());
|
||||
return false;
|
||||
}
|
||||
// Step 4.c. The spec algorithm simply returns undefined if proto is
|
||||
// null, but see the comment on GetNonexistentProperty.
|
||||
if (!proto)
|
||||
return GetNonexistentProperty(cx, obj, id, receiver, vp);
|
||||
|
||||
/* Don't warn if extra warnings not enabled or for random getprop operations. */
|
||||
if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM))
|
||||
return true;
|
||||
// Step 4.d. If the prototype is also native, this step is a
|
||||
// recursive tail call, and we don't need to go through all the
|
||||
// plumbing of JSObject::getGeneric; the top of the loop is where
|
||||
// we're going to end up anyway. But if pobj is non-native,
|
||||
// that optimization would be incorrect.
|
||||
if (!proto->isNative())
|
||||
return GeneralizedGetProperty(cx, proto, id, receiver, vp);
|
||||
|
||||
/* Don't warn repeatedly for the same script. */
|
||||
if (!script || script->warnedAboutUndefinedProp())
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Don't warn in self-hosted code (where the further presence of
|
||||
* JS::RuntimeOptions::werror() would result in impossible-to-avoid
|
||||
* errors to entirely-innocent client code).
|
||||
*/
|
||||
if (script->selfHosted())
|
||||
return true;
|
||||
|
||||
/* We may just be checking if that object has an iterator. */
|
||||
if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
|
||||
return true;
|
||||
|
||||
/* Do not warn about tests like (obj[prop] == undefined). */
|
||||
pc += js_CodeSpec[op].length;
|
||||
if (Detecting(cx, script, pc))
|
||||
return true;
|
||||
|
||||
unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
|
||||
script->setWarnedAboutUndefinedProp();
|
||||
|
||||
/* Ok, bad undefined property reference: whine about it. */
|
||||
RootedValue val(cx, IdToValue(id));
|
||||
if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
|
||||
JSDVG_IGNORE_STACK, val, js::NullPtr(),
|
||||
nullptr, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
pobj = &proto->as<NativeObject>();
|
||||
}
|
||||
|
||||
if (!obj2->isNative()) {
|
||||
if (!allowGC)
|
||||
return false;
|
||||
HandleObject obj2Handle = MaybeRooted<JSObject*, allowGC>::toHandle(obj2);
|
||||
HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver);
|
||||
HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id);
|
||||
MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp);
|
||||
return obj2->template is<ProxyObject>()
|
||||
? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle)
|
||||
: GetProperty(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
|
||||
}
|
||||
|
||||
typename MaybeRooted<NativeObject*, allowGC>::HandleType nobj2 =
|
||||
MaybeRooted<JSObject*, allowGC>::template downcastHandle<NativeObject>(obj2);
|
||||
|
||||
if (IsImplicitDenseOrTypedArrayElement(shape)) {
|
||||
vp.set(nobj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
|
||||
return true;
|
||||
}
|
||||
|
||||
// This call site is hot -- use the always-inlined variant of
|
||||
// NativeGetExistingProperty().
|
||||
if (!NativeGetExistingPropertyInline<allowGC>(cx, obj, receiver, nobj2, shape, vp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::NativeGetProperty(JSContext *cx, HandleNativeObject obj, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
/* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */
|
||||
return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
|
||||
return NativeGetPropertyInline<CanGC>(cx, obj, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::NativeGetPropertyNoGC(JSContext *cx, NativeObject *obj, JSObject *receiver, jsid id, Value *vp)
|
||||
{
|
||||
AutoAssertNoException nogc(cx);
|
||||
return GetPropertyHelperInline<NoGC>(cx, obj, receiver, id, vp);
|
||||
AutoAssertNoException noexc(cx);
|
||||
return NativeGetPropertyInline<NoGC>(cx, obj, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
js::NativeGetElement(JSContext *cx, HandleNativeObject obj, HandleObject receiver, uint32_t index,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
|
||||
/* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
|
||||
return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
|
||||
}
|
||||
/*** [[Set]] *************************************************************************************/
|
||||
|
||||
static bool
|
||||
MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
|
||||
@ -1796,9 +1842,6 @@ MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
|
||||
JSMSG_UNDECLARED_VAR, bytes.ptr());
|
||||
}
|
||||
|
||||
|
||||
/*** [[Set]] *************************************************************************************/
|
||||
|
||||
/*
|
||||
* When a [[Set]] operation finds no existing property with the given id
|
||||
* or finds a writable data property on the prototype chain, we end up here.
|
||||
@ -2069,7 +2112,8 @@ js::NativeSetProperty(JSContext *cx, HandleNativeObject obj, HandleObject receiv
|
||||
RootedNativeObject pobj(cx, obj);
|
||||
|
||||
// This loop isn't explicit in the spec algorithm. See the comment on step
|
||||
// 4.c.i below.
|
||||
// 4.c.i below. (There's a very similar loop in the NativeGetProperty
|
||||
// implementation, but unfortunately not similar enough to common up.)
|
||||
for (;;) {
|
||||
// Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
|
||||
bool done;
|
||||
@ -2128,6 +2172,9 @@ js::NativeSetElement(JSContext *cx, HandleNativeObject obj, HandleObject receive
|
||||
return NativeSetProperty(cx, obj, receiver, id, Qualified, vp, strict);
|
||||
}
|
||||
|
||||
|
||||
/* * */
|
||||
|
||||
bool
|
||||
js::NativeSetPropertyAttributes(JSContext *cx, HandleNativeObject obj, HandleId id,
|
||||
unsigned *attrsp)
|
||||
|
@ -1322,8 +1322,15 @@ extern bool
|
||||
NativeLookupElement(JSContext *cx, HandleNativeObject obj, uint32_t index,
|
||||
MutableHandleObject objp, MutableHandleShape propp);
|
||||
|
||||
/*
|
||||
* Get a property from `receiver`, after having already done a lookup and found
|
||||
* the property on a native object `obj`.
|
||||
*
|
||||
* `shape` must not be null and must not be an implicit dense property. It must
|
||||
* be present in obj's shape chain.
|
||||
*/
|
||||
extern bool
|
||||
NativeGetExistingProperty(JSContext *cx, HandleObject obj, HandleNativeObject pobj,
|
||||
NativeGetExistingProperty(JSContext *cx, HandleObject receiver, HandleNativeObject obj,
|
||||
HandleShape shape, MutableHandle<Value> vp);
|
||||
|
||||
extern bool
|
||||
|
@ -73,21 +73,10 @@ const JSSecurityCallbacks js::NullSecurityCallbacks = { };
|
||||
PerThreadData::PerThreadData(JSRuntime *runtime)
|
||||
: PerThreadDataFriendFields(),
|
||||
runtime_(runtime),
|
||||
jitTop(nullptr),
|
||||
jitJSContext(nullptr),
|
||||
jitActivation(nullptr),
|
||||
jitStackLimit_(0xbad),
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
traceLogger(nullptr),
|
||||
#endif
|
||||
activation_(nullptr),
|
||||
profilingActivation_(nullptr),
|
||||
asmJSActivationStack_(nullptr),
|
||||
autoFlushICache_(nullptr),
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
simulator_(nullptr),
|
||||
simulatorStackLimit_(0),
|
||||
#endif
|
||||
dtoaState(nullptr),
|
||||
suppressGC(0),
|
||||
#ifdef DEBUG
|
||||
@ -100,10 +89,6 @@ PerThreadData::~PerThreadData()
|
||||
{
|
||||
if (dtoaState)
|
||||
js_DestroyDtoaState(dtoaState);
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
js_delete(simulator_);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
@ -113,9 +98,6 @@ PerThreadData::init()
|
||||
if (!dtoaState)
|
||||
return false;
|
||||
|
||||
if (!regexpStack.init())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -132,6 +114,13 @@ ReturnZeroSize(const void *p)
|
||||
|
||||
JSRuntime::JSRuntime(JSRuntime *parentRuntime)
|
||||
: mainThread(this),
|
||||
jitTop(nullptr),
|
||||
jitJSContext(nullptr),
|
||||
jitActivation(nullptr),
|
||||
jitStackLimit_(0xbad),
|
||||
activation_(nullptr),
|
||||
profilingActivation_(nullptr),
|
||||
asmJSActivationStack_(nullptr),
|
||||
parentRuntime(parentRuntime),
|
||||
interrupt_(false),
|
||||
telemetryCallback(nullptr),
|
||||
@ -168,6 +157,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
|
||||
gc(thisFromCtor()),
|
||||
gcInitialized(false),
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
simulator_(nullptr),
|
||||
simulatorStackLimit_(0),
|
||||
simulatorRuntime_(nullptr),
|
||||
#endif
|
||||
scriptAndCountsVector(nullptr),
|
||||
@ -274,6 +265,9 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||
if (!mainThread.init())
|
||||
return false;
|
||||
|
||||
if (!regexpStack.init())
|
||||
return false;
|
||||
|
||||
js::TlsPerThreadData.set(&mainThread);
|
||||
|
||||
if (CanUseExtraThreads())
|
||||
@ -442,6 +436,7 @@ JSRuntime::~JSRuntime()
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
js::jit::DestroySimulatorRuntime(simulatorRuntime_);
|
||||
js_delete(simulator_);
|
||||
#endif
|
||||
|
||||
DebugOnly<size_t> oldCount = liveRuntimesCount--;
|
||||
@ -594,7 +589,7 @@ InvokeInterruptCallback(JSContext *cx)
|
||||
}
|
||||
|
||||
void
|
||||
PerThreadData::resetJitStackLimit()
|
||||
JSRuntime::resetJitStackLimit()
|
||||
{
|
||||
// Note that, for now, we use the untrusted limit for ion. This is fine,
|
||||
// because it's the most conservative limit, and if we hit it, we'll bail
|
||||
@ -602,12 +597,12 @@ PerThreadData::resetJitStackLimit()
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
jitStackLimit_ = jit::Simulator::StackLimit();
|
||||
#else
|
||||
jitStackLimit_ = nativeStackLimit[StackForUntrustedScript];
|
||||
jitStackLimit_ = mainThread.nativeStackLimit[StackForUntrustedScript];
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
PerThreadData::initJitStackLimit()
|
||||
JSRuntime::initJitStackLimit()
|
||||
{
|
||||
resetJitStackLimit();
|
||||
}
|
||||
@ -616,7 +611,7 @@ void
|
||||
JSRuntime::requestInterrupt(InterruptMode mode)
|
||||
{
|
||||
interrupt_ = true;
|
||||
mainThread.jitStackLimit_ = UINTPTR_MAX;
|
||||
jitStackLimit_ = UINTPTR_MAX;
|
||||
|
||||
if (mode == JSRuntime::RequestInterruptUrgent)
|
||||
InterruptRunningJitCode(this);
|
||||
@ -626,9 +621,9 @@ bool
|
||||
JSRuntime::handleInterrupt(JSContext *cx)
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) {
|
||||
if (interrupt_ || jitStackLimit_ == UINTPTR_MAX) {
|
||||
interrupt_ = false;
|
||||
mainThread.resetJitStackLimit();
|
||||
resetJitStackLimit();
|
||||
return InvokeInterruptCallback(cx);
|
||||
}
|
||||
return true;
|
||||
|
@ -481,6 +481,11 @@ void DisableExtraThreads();
|
||||
*/
|
||||
class PerThreadData : public PerThreadDataFriendFields
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Grant access to runtime_.
|
||||
friend void js::AssertCurrentThreadCanLock(RuntimeLock which);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Backpointer to the full shared JSRuntime* with which this
|
||||
* thread is associated. This is private because accessing the
|
||||
@ -507,112 +512,14 @@ class PerThreadData : public PerThreadDataFriendFields
|
||||
js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If Baseline or Ion code is on the stack, and has called into C++, this
|
||||
* will be aligned to an exit frame.
|
||||
*/
|
||||
uint8_t *jitTop;
|
||||
|
||||
/*
|
||||
* The current JSContext when entering JIT code. This field may only be used
|
||||
* from JIT code and C++ directly called by JIT code (otherwise it may refer
|
||||
* to the wrong JSContext).
|
||||
*/
|
||||
JSContext *jitJSContext;
|
||||
|
||||
/*
|
||||
* Points to the most recent JitActivation pushed on the thread.
|
||||
* See JitActivation constructor in vm/Stack.cpp
|
||||
*/
|
||||
js::jit::JitActivation *jitActivation;
|
||||
|
||||
/* See comment for JSRuntime::interrupt_. */
|
||||
private:
|
||||
mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
|
||||
void resetJitStackLimit();
|
||||
friend struct ::JSRuntime;
|
||||
public:
|
||||
void initJitStackLimit();
|
||||
|
||||
uintptr_t jitStackLimit() const { return jitStackLimit_; }
|
||||
|
||||
// For read-only JIT use:
|
||||
void *addressOfJitStackLimit() { return &jitStackLimit_; }
|
||||
static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); }
|
||||
|
||||
// Information about the heap allocated backtrack stack used by RegExp JIT code.
|
||||
irregexp::RegExpStack regexpStack;
|
||||
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
TraceLoggerThread *traceLogger;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class js::Activation;
|
||||
friend class js::ActivationIterator;
|
||||
friend class js::jit::JitActivation;
|
||||
friend class js::AsmJSActivation;
|
||||
friend class js::jit::CompileRuntime;
|
||||
#ifdef DEBUG
|
||||
friend void js::AssertCurrentThreadCanLock(RuntimeLock which);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Points to the most recent activation running on the thread.
|
||||
* See Activation comment in vm/Stack.h.
|
||||
*/
|
||||
js::Activation *activation_;
|
||||
|
||||
/*
|
||||
* Points to the most recent profiling activation running on the
|
||||
* thread.
|
||||
*/
|
||||
js::Activation * volatile profilingActivation_;
|
||||
|
||||
/* See AsmJSActivation comment. */
|
||||
js::AsmJSActivation * volatile asmJSActivationStack_;
|
||||
|
||||
/* Pointer to the current AutoFlushICache. */
|
||||
js::jit::AutoFlushICache *autoFlushICache_;
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
js::jit::Simulator *simulator_;
|
||||
uintptr_t simulatorStackLimit_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
js::Activation *const *addressOfActivation() const {
|
||||
return &activation_;
|
||||
}
|
||||
static unsigned offsetOfAsmJSActivationStackReadOnly() {
|
||||
return offsetof(PerThreadData, asmJSActivationStack_);
|
||||
}
|
||||
static unsigned offsetOfActivation() {
|
||||
return offsetof(PerThreadData, activation_);
|
||||
}
|
||||
|
||||
js::Activation *profilingActivation() const {
|
||||
return profilingActivation_;
|
||||
}
|
||||
void *addressOfProfilingActivation() {
|
||||
return (void*) &profilingActivation_;
|
||||
}
|
||||
static unsigned offsetOfProfilingActivation() {
|
||||
return offsetof(PerThreadData, profilingActivation_);
|
||||
}
|
||||
|
||||
js::AsmJSActivation *asmJSActivationStack() const {
|
||||
return asmJSActivationStack_;
|
||||
}
|
||||
static js::AsmJSActivation *innermostAsmJSActivation() {
|
||||
PerThreadData *ptd = TlsPerThreadData.get();
|
||||
return ptd ? ptd->asmJSActivationStack_ : nullptr;
|
||||
}
|
||||
|
||||
js::Activation *activation() const {
|
||||
return activation_;
|
||||
}
|
||||
|
||||
/* State used by jsdtoa.cpp. */
|
||||
DtoaState *dtoaState;
|
||||
|
||||
@ -659,21 +566,10 @@ class PerThreadData : public PerThreadDataFriendFields
|
||||
{
|
||||
MOZ_ASSERT(!pt->runtime_);
|
||||
pt->runtime_ = rt;
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
// The simulator has a pointer to its SimulatorRuntime, but helper threads
|
||||
// don't have a simulator as they don't run JIT code so this pointer need not
|
||||
// be updated. All the paths that the helper threads use access the
|
||||
// SimulatorRuntime via the PerThreadData.
|
||||
MOZ_ASSERT(!pt->simulator_);
|
||||
#endif
|
||||
}
|
||||
|
||||
~AutoEnterRuntime() {
|
||||
pt->runtime_ = nullptr;
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
// Check that helper threads have not run JIT code and/or added a simulator.
|
||||
MOZ_ASSERT(!pt->simulator_);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@ -684,7 +580,6 @@ class PerThreadData : public PerThreadDataFriendFields
|
||||
js::jit::Simulator *simulator() const;
|
||||
void setSimulator(js::jit::Simulator *sim);
|
||||
js::jit::SimulatorRuntime *simulatorRuntime() const;
|
||||
uintptr_t *addressOfSimulatorStackLimit();
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -707,6 +602,97 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
*/
|
||||
js::PerThreadData mainThread;
|
||||
|
||||
/*
|
||||
* If Baseline or Ion code is on the stack, and has called into C++, this
|
||||
* will be aligned to an exit frame.
|
||||
*/
|
||||
uint8_t *jitTop;
|
||||
|
||||
/*
|
||||
* The current JSContext when entering JIT code. This field may only be used
|
||||
* from JIT code and C++ directly called by JIT code (otherwise it may refer
|
||||
* to the wrong JSContext).
|
||||
*/
|
||||
JSContext *jitJSContext;
|
||||
|
||||
/*
|
||||
* Points to the most recent JitActivation pushed on the thread.
|
||||
* See JitActivation constructor in vm/Stack.cpp
|
||||
*/
|
||||
js::jit::JitActivation *jitActivation;
|
||||
|
||||
/* See comment for JSRuntime::interrupt_. */
|
||||
private:
|
||||
mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
|
||||
void resetJitStackLimit();
|
||||
|
||||
public:
|
||||
void initJitStackLimit();
|
||||
|
||||
uintptr_t jitStackLimit() const { return jitStackLimit_; }
|
||||
|
||||
// For read-only JIT use:
|
||||
void *addressOfJitStackLimit() { return &jitStackLimit_; }
|
||||
static size_t offsetOfJitStackLimit() { return offsetof(JSRuntime, jitStackLimit_); }
|
||||
|
||||
// Information about the heap allocated backtrack stack used by RegExp JIT code.
|
||||
js::irregexp::RegExpStack regexpStack;
|
||||
|
||||
private:
|
||||
friend class js::Activation;
|
||||
friend class js::ActivationIterator;
|
||||
friend class js::jit::JitActivation;
|
||||
friend class js::AsmJSActivation;
|
||||
friend class js::jit::CompileRuntime;
|
||||
#ifdef DEBUG
|
||||
friend void js::AssertCurrentThreadCanLock(js::RuntimeLock which);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Points to the most recent activation running on the thread.
|
||||
* See Activation comment in vm/Stack.h.
|
||||
*/
|
||||
js::Activation *activation_;
|
||||
|
||||
/*
|
||||
* Points to the most recent profiling activation running on the
|
||||
* thread.
|
||||
*/
|
||||
js::Activation * volatile profilingActivation_;
|
||||
|
||||
/* See AsmJSActivation comment. */
|
||||
js::AsmJSActivation * volatile asmJSActivationStack_;
|
||||
|
||||
public:
|
||||
js::Activation *const *addressOfActivation() const {
|
||||
return &activation_;
|
||||
}
|
||||
static unsigned offsetOfActivation() {
|
||||
return offsetof(JSRuntime, activation_);
|
||||
}
|
||||
|
||||
js::Activation *profilingActivation() const {
|
||||
return profilingActivation_;
|
||||
}
|
||||
void *addressOfProfilingActivation() {
|
||||
return (void*) &profilingActivation_;
|
||||
}
|
||||
static unsigned offsetOfProfilingActivation() {
|
||||
return offsetof(JSRuntime, profilingActivation_);
|
||||
}
|
||||
|
||||
js::AsmJSActivation *asmJSActivationStack() const {
|
||||
return asmJSActivationStack_;
|
||||
}
|
||||
static js::AsmJSActivation *innermostAsmJSActivation() {
|
||||
js::PerThreadData *ptd = js::TlsPerThreadData.get();
|
||||
return ptd ? ptd->runtimeFromMainThread()->asmJSActivationStack_ : nullptr;
|
||||
}
|
||||
|
||||
js::Activation *activation() const {
|
||||
return activation_;
|
||||
}
|
||||
|
||||
/*
|
||||
* If non-null, another runtime guaranteed to outlive this one and whose
|
||||
* permanent data may be used by this one where possible.
|
||||
@ -988,6 +974,8 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
}
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
js::jit::Simulator *simulator_;
|
||||
uintptr_t simulatorStackLimit_;
|
||||
js::jit::SimulatorRuntime *simulatorRuntime_;
|
||||
#endif
|
||||
|
||||
@ -997,6 +985,9 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
}
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
js::jit::Simulator *simulator() const;
|
||||
void setSimulator(js::jit::Simulator *sim);
|
||||
uintptr_t *addressOfSimulatorStackLimit();
|
||||
js::jit::SimulatorRuntime *simulatorRuntime() const;
|
||||
void setSimulatorRuntime(js::jit::SimulatorRuntime *srt);
|
||||
#endif
|
||||
@ -1438,7 +1429,7 @@ namespace js {
|
||||
static inline JSContext *
|
||||
GetJSContextFromJitCode()
|
||||
{
|
||||
JSContext *cx = TlsPerThreadData.get()->jitJSContext;
|
||||
JSContext *cx = js::TlsPerThreadData.get()->runtimeFromMainThread()->jitJSContext;
|
||||
MOZ_ASSERT(cx);
|
||||
return cx;
|
||||
}
|
||||
|
@ -99,9 +99,9 @@ SPSProfiler::enable(bool enabled)
|
||||
/* Update lastProfilingFrame to point to the top-most JS jit-frame currently on
|
||||
* stack.
|
||||
*/
|
||||
if (rt->mainThread.jitActivation) {
|
||||
void *lastProfilingFrame = GetTopProfilingJitFrame(rt->mainThread.jitTop);
|
||||
rt->mainThread.jitActivation->setLastProfilingFrame(lastProfilingFrame);
|
||||
if (rt->jitActivation) {
|
||||
void *lastProfilingFrame = GetTopProfilingJitFrame(rt->jitTop);
|
||||
rt->jitActivation->setLastProfilingFrame(lastProfilingFrame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,21 +33,6 @@ StackBaseShape::StackBaseShape(ExclusiveContext *cx, const Class *clasp,
|
||||
compartment(cx->compartment_)
|
||||
{}
|
||||
|
||||
inline bool
|
||||
Shape::get(JSContext* cx, HandleObject receiver, JSObject* obj, JSObject *pobj,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
MOZ_ASSERT(!hasDefaultGetter());
|
||||
|
||||
if (hasGetterValue()) {
|
||||
Value fval = getterValue();
|
||||
return InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp);
|
||||
}
|
||||
|
||||
RootedId id(cx, propid());
|
||||
return CallJSPropertyOp(cx, getterOp(), receiver, id, vp);
|
||||
}
|
||||
|
||||
inline Shape *
|
||||
Shape::search(ExclusiveContext *cx, jsid id)
|
||||
{
|
||||
|
@ -917,7 +917,6 @@ class Shape : public gc::TenuredCell
|
||||
setter() == rawSetter;
|
||||
}
|
||||
|
||||
bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp);
|
||||
bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp);
|
||||
|
||||
BaseShape *base() const { return base_.get(); }
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user