diff --git a/accessible/xpcom/xpcAccessible.cpp b/accessible/xpcom/xpcAccessible.cpp index da6d42d6084b..66abcc7cfd6f 100644 --- a/accessible/xpcom/xpcAccessible.cpp +++ b/accessible/xpcom/xpcAccessible.cpp @@ -199,8 +199,12 @@ NS_IMETHODIMP xpcAccessible::GetState(uint32_t* aState, uint32_t* aExtraState) { NS_ENSURE_ARG_POINTER(aState); + + if (!Intl()) + nsAccUtils::To32States(states::DEFUNCT, aState, aExtraState); + else + nsAccUtils::To32States(Intl()->State(), aState, aExtraState); - nsAccUtils::To32States(Intl()->State(), aState, aExtraState); return NS_OK; } diff --git a/caps/nsPrincipal.cpp b/caps/nsPrincipal.cpp index c2d07b5b88d9..bcbc909f2c75 100644 --- a/caps/nsPrincipal.cpp +++ b/caps/nsPrincipal.cpp @@ -17,6 +17,7 @@ #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" #include "nsIClassInfoImpl.h" +#include "nsIProtocolHandler.h" #include "nsError.h" #include "nsIContentSecurityPolicy.h" #include "jswrapper.h" @@ -490,6 +491,18 @@ nsPrincipal::GetBaseDomain(nsACString& aBaseDomain) } } + bool hasNoRelativeFlag; + nsresult rv = NS_URIChainHasFlags(mCodebase, + nsIProtocolHandler::URI_NORELATIVE, + &hasNoRelativeFlag); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasNoRelativeFlag) { + return mCodebase->GetSpec(aBaseDomain); + } + // For everything else, we ask the TLD service via // the ThirdPartyUtil. nsCOMPtr thirdPartyUtil = diff --git a/dom/base/nsCSPParser.cpp b/dom/base/nsCSPParser.cpp index 3fe7207492e8..9ff6c6143471 100644 --- a/dom/base/nsCSPParser.cpp +++ b/dom/base/nsCSPParser.cpp @@ -646,21 +646,26 @@ nsCSPParser::sourceExpression() // mCurValue = "" resetCurValue(); - // If mCurToken does not provide a scheme, we apply the scheme from selfURI + // If mCurToken does not provide a scheme (scheme-less source), we apply the scheme + // from selfURI but we also need to remember if the protected resource is http, in + // which case we should allow https loads, see: + // http://www.w3.org/TR/CSP2/#match-source-expression + bool allowHttps = false; if (parsedScheme.IsEmpty()) { // Resetting internal helpers, because we might already have parsed some of the host // when trying to parse a scheme. resetCurChar(mCurToken); - nsAutoCString scheme; - mSelfURI->GetScheme(scheme); - parsedScheme.AssignASCII(scheme.get()); + nsAutoCString selfScheme; + mSelfURI->GetScheme(selfScheme); + parsedScheme.AssignASCII(selfScheme.get()); + allowHttps = selfScheme.EqualsASCII("http"); } // At this point we are expecting a host to be parsed. // Trying to create a new nsCSPHost. if (nsCSPHostSrc *cspHost = hostSource()) { // Do not forget to set the parsed scheme. - cspHost->setScheme(parsedScheme); + cspHost->setScheme(parsedScheme, allowHttps); return cspHost; } // Error was reported in hostSource() diff --git a/dom/base/nsCSPUtils.cpp b/dom/base/nsCSPUtils.cpp index fb8b30e54b01..30efd02cbfa6 100644 --- a/dom/base/nsCSPUtils.cpp +++ b/dom/base/nsCSPUtils.cpp @@ -279,6 +279,7 @@ nsCSPSchemeSrc::toString(nsAString& outStr) const nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost) : mHost(aHost) + , mAllowHttps(false) { ToLowerCase(mHost); } @@ -307,7 +308,16 @@ nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected NS_ENSURE_SUCCESS(rv, false); if (!mScheme.IsEmpty() && !mScheme.EqualsASCII(scheme.get())) { - return false; + + // We should not return false for scheme-less sources where the protected resource + // is http and the load is https, see: + // http://www.w3.org/TR/CSP2/#match-source-expression + bool isHttpsScheme = + (NS_SUCCEEDED(aUri->SchemeIs("https", &isHttpsScheme)) && isHttpsScheme); + + if (!(isHttpsScheme && mAllowHttps)) { + return false; + } } // The host in nsCSpHostSrc should never be empty. In case we are enforcing @@ -386,7 +396,13 @@ nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected if (mPort.IsEmpty()) { int32_t port = NS_GetDefaultPort(NS_ConvertUTF16toUTF8(mScheme).get()); if (port != uriPort) { - return false; + // We should not return false for scheme-less sources where the protected resource + // is http and the load is https, see: http://www.w3.org/TR/CSP2/#match-source-expression + // BUT, we only allow scheme-less sources to be upgraded from http to https if CSP + // does not explicitly define a port. + if (!(uriPort == NS_GetDefaultPort("https") && mAllowHttps)) { + return false; + } } } // 4.7) Port matching: Compare the ports. @@ -431,10 +447,11 @@ nsCSPHostSrc::toString(nsAString& outStr) const } void -nsCSPHostSrc::setScheme(const nsAString& aScheme) +nsCSPHostSrc::setScheme(const nsAString& aScheme, bool aAllowHttps) { mScheme = aScheme; ToLowerCase(mScheme); + mAllowHttps = aAllowHttps; } void diff --git a/dom/base/nsCSPUtils.h b/dom/base/nsCSPUtils.h index bed6e282a52b..a1510c408153 100644 --- a/dom/base/nsCSPUtils.h +++ b/dom/base/nsCSPUtils.h @@ -220,7 +220,7 @@ class nsCSPHostSrc : public nsCSPBaseSrc { bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected) const; void toString(nsAString& outStr) const; - void setScheme(const nsAString& aScheme); + void setScheme(const nsAString& aScheme, bool aAllowHttps = false); void setPort(const nsAString& aPort); void appendPath(const nsAString &aPath); @@ -229,6 +229,7 @@ class nsCSPHostSrc : public nsCSPBaseSrc { nsString mHost; nsString mPort; nsString mPath; + bool mAllowHttps; }; /* =============== nsCSPKeywordSrc ============ */ diff --git a/dom/base/nsXMLHttpRequest.h b/dom/base/nsXMLHttpRequest.h index b4860b71f93a..a0628527b406 100644 --- a/dom/base/nsXMLHttpRequest.h +++ b/dom/base/nsXMLHttpRequest.h @@ -427,46 +427,61 @@ private: bool IsDeniedCrossSiteRequest(); public: - void Send(ErrorResult& aRv) + void Send(JSContext* /*aCx*/, ErrorResult& aRv) { aRv = Send(Nullable()); } - void Send(const mozilla::dom::ArrayBuffer& aArrayBuffer, ErrorResult& aRv) + void Send(JSContext* /*aCx*/, + const mozilla::dom::ArrayBuffer& aArrayBuffer, + ErrorResult& aRv) { aRv = Send(RequestBody(&aArrayBuffer)); } - void Send(const mozilla::dom::ArrayBufferView& aArrayBufferView, + void Send(JSContext* /*aCx*/, + const mozilla::dom::ArrayBufferView& aArrayBufferView, ErrorResult& aRv) { aRv = Send(RequestBody(&aArrayBufferView)); } - void Send(mozilla::dom::File& aBlob, ErrorResult& aRv) + void Send(JSContext* /*aCx*/, mozilla::dom::File& aBlob, ErrorResult& aRv) { aRv = Send(RequestBody(aBlob)); } - void Send(nsIDocument& aDoc, ErrorResult& aRv) + void Send(JSContext* /*aCx*/, nsIDocument& aDoc, ErrorResult& aRv) { aRv = Send(RequestBody(&aDoc)); } - void Send(const nsAString& aString, ErrorResult& aRv) + void Send(JSContext* aCx, const nsAString& aString, ErrorResult& aRv) { if (DOMStringIsNull(aString)) { - Send(aRv); + Send(aCx, aRv); } else { aRv = Send(RequestBody(aString)); } } - void Send(nsFormData& aFormData, ErrorResult& aRv) + void Send(JSContext* /*aCx*/, nsFormData& aFormData, ErrorResult& aRv) { aRv = Send(RequestBody(aFormData)); } - void Send(nsIInputStream* aStream, ErrorResult& aRv) + void Send(JSContext* aCx, nsIInputStream* aStream, ErrorResult& aRv) { NS_ASSERTION(aStream, "Null should go to string version"); nsCOMPtr wjs = do_QueryInterface(aStream); if (wjs) { - aRv.Throw(NS_ERROR_DOM_TYPE_ERR); + JSObject* data = wjs->GetJSObject(); + if (!data) { + aRv.Throw(NS_ERROR_DOM_TYPE_ERR); + return; + } + JS::Rooted dataAsValue(aCx, JS::ObjectValue(*data)); + nsAutoString dataAsString; + if (ConvertJSValueToString(aCx, dataAsValue, mozilla::dom::eNull, + mozilla::dom::eNull, dataAsString)) { + Send(aCx, dataAsString, aRv); + } else { + aRv.Throw(NS_ERROR_FAILURE); + } return; } aRv = Send(RequestBody(aStream)); diff --git a/dom/base/test/csp/file_csp_allow_https_schemes.html b/dom/base/test/csp/file_csp_allow_https_schemes.html new file mode 100644 index 000000000000..96dab5013a98 --- /dev/null +++ b/dom/base/test/csp/file_csp_allow_https_schemes.html @@ -0,0 +1,14 @@ + + + + Bug 826805 - CSP: Allow http and https for scheme-less sources + + +
blocked
+ + + + diff --git a/dom/base/test/csp/mochitest.ini b/dom/base/test/csp/mochitest.ini index 6c99d1bf46d1..3c717dc4bc27 100644 --- a/dom/base/test/csp/mochitest.ini +++ b/dom/base/test/csp/mochitest.ini @@ -4,6 +4,7 @@ support-files = file_connect-src.html file_CSP.css file_CSP.sjs + file_csp_allow_https_schemes.html file_CSP_bug663567.xsl file_CSP_bug663567_allows.xml file_CSP_bug663567_allows.xml^headers^ @@ -103,6 +104,8 @@ support-files = [test_base-uri.html] [test_connect-src.html] [test_CSP.html] +[test_csp_allow_https_schemes.html] +skip-if = buildapp == 'b2g' #no ssl support [test_CSP_bug663567.html] [test_CSP_bug802872.html] [test_CSP_bug885433.html] diff --git a/dom/base/test/csp/test_connect-src.html b/dom/base/test/csp/test_connect-src.html index f271917afac4..837065e976b3 100644 --- a/dom/base/test/csp/test_connect-src.html +++ b/dom/base/test/csp/test_connect-src.html @@ -91,6 +91,7 @@ function loadNextTest() { if (counter == tests.length) { window.ConnectSrcExaminer.remove(); SimpleTest.finish(); + return; } var src = "file_csp_testserver.sjs"; diff --git a/dom/base/test/csp/test_csp_allow_https_schemes.html b/dom/base/test/csp/test_csp_allow_https_schemes.html new file mode 100644 index 000000000000..a7784f5d2073 --- /dev/null +++ b/dom/base/test/csp/test_csp_allow_https_schemes.html @@ -0,0 +1,71 @@ + + + + Bug 826805 - Allow http and https for scheme-less sources + + + + + +

+ + + + + diff --git a/dom/base/test/echo.sjs b/dom/base/test/echo.sjs new file mode 100644 index 000000000000..6e3242c9f72e --- /dev/null +++ b/dom/base/test/echo.sjs @@ -0,0 +1,21 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); + +function handleRequest(request, response) +{ + response.setHeader("Content-Type", "text/plain"); + if (request.method == "GET") { + response.write(request.queryString); + return; + } + + var bodyStream = new BinaryInputStream(request.bodyInputStream); + var body = ""; + var bodyAvail; + while ((bodyAvail = bodyStream.available()) > 0) + body += String.fromCharCode.apply(null, bodyStream.readByteArray(bodyAvail)); + + response.write(body); +} diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 383e987a9261..c8c813816a46 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -56,6 +56,7 @@ support-files = bug819051.sjs copypaste.js delayedServerEvents.sjs + echo.sjs eventsource.resource eventsource.resource^headers^ eventsource_redirect.resource @@ -384,7 +385,6 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || (os == 'linux' && debug & [test_bug340571.html] [test_bug343596.html] [test_bug345339.html] -skip-if = e10s # Bug 1081453 - shutdown leaks with e10s and WebIDL dom::File [test_bug346485.html] [test_bug352728.html] [test_bug352728.xhtml] @@ -739,6 +739,7 @@ skip-if = toolkit == 'android' [test_xhr_forbidden_headers.html] [test_xhr_progressevents.html] skip-if = toolkit == 'android' +[test_xhr_send.html] [test_xhr_send_readystate.html] [test_xhr_withCredentials.html] [test_file_from_blob.html] diff --git a/dom/base/test/test_xhr_send.html b/dom/base/test/test_xhr_send.html new file mode 100644 index 000000000000..7d8e1bae8483 --- /dev/null +++ b/dom/base/test/test_xhr_send.html @@ -0,0 +1,83 @@ + + + + + + Test for Bug 1096263 + + + + + +Mozilla Bug 1096263 +

+
+ +
+
+
+ + diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 33b4ea85d9e4..3336922c6b4c 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1551,7 +1551,7 @@ DOMInterfaces = { 'XMLHttpRequest': [ { 'nativeType': 'nsXMLHttpRequest', - 'implicitJSContext': [ 'constructor', ], + 'implicitJSContext': [ 'constructor', 'send'], }, { 'workers': True, diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 7e9596bcfa37..b8b78b56fad1 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -409,7 +409,7 @@ class CGDOMJSClass(CGThing): nullptr, /* deleteGeneric */ nullptr, /* watch */ nullptr, /* unwatch */ - nullptr, /* slice */ + nullptr, /* getElements */ nullptr, /* enumerate */ JS_ObjectToOuterObject /* thisObject */ } @@ -10716,7 +10716,7 @@ class CGDOMJSProxyHandler_finalize(ClassMethod): finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define()) -class CGDOMJSProxyHandler_slice(ClassMethod): +class CGDOMJSProxyHandler_getElements(ClassMethod): def __init__(self, descriptor): assert descriptor.supportsIndexedProperties() @@ -10724,8 +10724,8 @@ class CGDOMJSProxyHandler_slice(ClassMethod): Argument('JS::Handle', 'proxy'), Argument('uint32_t', 'begin'), Argument('uint32_t', 'end'), - Argument('JS::Handle', 'array')] - ClassMethod.__init__(self, "slice", "bool", args, virtual=True, override=True, const=True) + Argument('js::ElementAdder*', 'adder')] + ClassMethod.__init__(self, "getElements", "bool", args, virtual=True, override=True, const=True) self.descriptor = descriptor def getBody(self): @@ -10738,7 +10738,7 @@ class CGDOMJSProxyHandler_slice(ClassMethod): 'jsvalRef': 'temp', 'jsvalHandle': '&temp', 'obj': 'proxy', - 'successCode': ("js::UnsafeDefineElement(cx, array, index - begin, temp);\n" + 'successCode': ("adder->append(cx, temp);\n" "continue;\n") } get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define() @@ -10763,7 +10763,7 @@ class CGDOMJSProxyHandler_slice(ClassMethod): if (!js::GetObjectProto(cx, proxy, &proto)) { return false; } - return js::SliceSlowly(cx, proto, proxy, ourEnd, end, array); + return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder); } return true; @@ -10836,7 +10836,7 @@ class CGDOMJSProxyHandler(CGClass): if descriptor.supportsIndexedProperties(): - methods.append(CGDOMJSProxyHandler_slice(descriptor)) + methods.append(CGDOMJSProxyHandler_getElements(descriptor)) if (descriptor.operations['IndexedSetter'] is not None or (descriptor.operations['NamedSetter'] is not None and descriptor.interface.getExtendedAttribute('OverrideBuiltins'))): diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index a02236377eb9..d04d54104b21 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -236,13 +236,15 @@ class HTMLInputElementState MOZ_FINAL : public nsISupports mValue = aValue; } - const nsTArray>& GetFiles() { - return mFiles; + const nsTArray>& GetFileImpls() { + return mFileImpls; } - void SetFiles(const nsTArray>& aFiles) { - mFiles.Clear(); - mFiles.AppendElements(aFiles); + void SetFileImpls(const nsTArray>& aFile) { + mFileImpls.Clear(); + for (uint32_t i = 0, len = aFile.Length(); i < len; ++i) { + mFileImpls.AppendElement(aFile[i]->Impl()); + } } HTMLInputElementState() @@ -255,7 +257,7 @@ class HTMLInputElementState MOZ_FINAL : public nsISupports ~HTMLInputElementState() {} nsString mValue; - nsTArray> mFiles; + nsTArray> mFileImpls; bool mChecked; bool mCheckedSet; }; @@ -5697,7 +5699,7 @@ HTMLInputElement::SaveState() case VALUE_MODE_FILENAME: if (!mFiles.IsEmpty()) { inputState = new HTMLInputElementState(); - inputState->SetFiles(mFiles); + inputState->SetFileImpls(mFiles); } break; case VALUE_MODE_VALUE: @@ -5900,7 +5902,17 @@ HTMLInputElement::RestoreState(nsPresState* aState) break; case VALUE_MODE_FILENAME: { - const nsTArray>& files = inputState->GetFiles(); + const nsTArray>& fileImpls = inputState->GetFileImpls(); + + nsCOMPtr global = OwnerDoc()->GetScopeObject(); + MOZ_ASSERT(global); + + nsTArray> files; + for (uint32_t i = 0, len = fileImpls.Length(); i < len; ++i) { + nsRefPtr file = new File(global, fileImpls[i]); + files.AppendElement(file); + } + SetFiles(files, true); } break; diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index d73dc3280f73..b6ca0bbec680 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -65,7 +65,7 @@ PRLogModuleInfo* gMediaStreamGraphLog; /** * The singleton graph instance. */ -static MediaStreamGraphImpl* gGraph; +static nsDataHashtable gGraphs; MediaStreamGraphImpl::~MediaStreamGraphImpl() { @@ -1636,9 +1636,10 @@ MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG) NS_DispatchToMainThread(event); LIFECYCLE_LOG("Disconnecting MediaStreamGraph %p", this); - if (this == gGraph) { + MediaStreamGraphImpl* graph; + if (gGraphs.Get(mAudioChannel, &graph) && graph == this) { // null out gGraph if that's the graph being shut down - gGraph = nullptr; + gGraphs.Remove(mAudioChannel); } } } else { @@ -1789,9 +1790,12 @@ MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage) delete aMessage; if (IsEmpty() && mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) { - if (gGraph == this) { - gGraph = nullptr; + + MediaStreamGraphImpl* graph; + if (gGraphs.Get(mAudioChannel, &graph) && graph == this) { + gGraphs.Remove(mAudioChannel); } + Destroy(); } return; @@ -2741,6 +2745,7 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, #ifdef DEBUG , mCanRunMessagesSynchronously(false) #endif + , mAudioChannel(static_cast(aChannel)) { #ifdef PR_LOGGING if (!gMediaStreamGraphLog) { @@ -2779,15 +2784,26 @@ NS_IMPL_ISUPPORTS(MediaStreamGraphShutdownObserver, nsIObserver) static bool gShutdownObserverRegistered = false; +namespace { + +PLDHashOperator +ForceShutdownEnumerator(const uint32_t& /* aAudioChannel */, + MediaStreamGraphImpl* aGraph, + void* /* aUnused */) +{ + aGraph->ForceShutDown(); + return PL_DHASH_NEXT; +} + +} // anonymous namespace + NS_IMETHODIMP MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) { if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { - if (gGraph) { - gGraph->ForceShutDown(); - } + gGraphs.EnumerateRead(ForceShutdownEnumerator, nullptr); nsContentUtils::UnregisterShutdownObserver(this); gShutdownObserverRegistered = false; } @@ -2799,7 +2815,10 @@ MediaStreamGraph::GetInstance(DOMMediaStream::TrackTypeHints aHint, dom::AudioCh { NS_ASSERTION(NS_IsMainThread(), "Main thread only"); - if (!gGraph) { + uint32_t channel = static_cast(aChannel); + MediaStreamGraphImpl* graph = nullptr; + + if (!gGraphs.Get(channel, &graph)) { if (!gShutdownObserverRegistered) { gShutdownObserverRegistered = true; nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver()); @@ -2807,12 +2826,13 @@ MediaStreamGraph::GetInstance(DOMMediaStream::TrackTypeHints aHint, dom::AudioCh CubebUtils::InitPreferredSampleRate(); - gGraph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel); + graph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel); + gGraphs.Put(channel, graph); - STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph)); + STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", graph)); } - return gGraph; + return graph; } MediaStreamGraph* @@ -2995,7 +3015,10 @@ MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine, bool MediaStreamGraph::IsNonRealtime() const { - return this != gGraph; + const MediaStreamGraphImpl* impl = static_cast(this); + MediaStreamGraphImpl* graph; + + return !gGraphs.Get(impl->AudioChannel(), &graph) || graph != impl; } void diff --git a/dom/media/MediaStreamGraphImpl.h b/dom/media/MediaStreamGraphImpl.h index 628f327557b8..f7fbe2887cde 100644 --- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -661,6 +661,8 @@ public: nsRefPtr mFarendObserverRef; #endif + uint32_t AudioChannel() const { return mAudioChannel; } + private: virtual ~MediaStreamGraphImpl(); @@ -694,6 +696,9 @@ private: bool mCanRunMessagesSynchronously; #endif + // We use uint32_t instead AudioChannel because this is just used as key for + // the hashtable gGraphs. + uint32_t mAudioChannel; }; } diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index 8b5011d91385..cfbd7b890296 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -374,6 +374,7 @@ skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !de skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 [test_fastSeek-forwards.html] [test_imagecapture.html] +skip-if = toolkit == 'android' # bug 1071217 - crashes on 2.3 [test_info_leak.html] [test_invalid_reject.html] [test_invalid_reject_play.html] diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp index 8cfb65de65bf..8fc28b368a65 100644 --- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -654,12 +654,6 @@ AudioContext::MozAudioChannelType() const return mDestination->MozAudioChannelType(); } -void -AudioContext::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv) -{ - mDestination->SetMozAudioChannelType(aValue, aRv); -} - AudioChannel AudioContext::TestAudioChannelInAudioNodeStream() { diff --git a/dom/media/webaudio/AudioContext.h b/dom/media/webaudio/AudioContext.h index 42dda249ca4b..ecac9ae107f6 100644 --- a/dom/media/webaudio/AudioContext.h +++ b/dom/media/webaudio/AudioContext.h @@ -222,7 +222,6 @@ public: JSObject* GetGlobalJSObject() const; AudioChannel MozAudioChannelType() const; - void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv); AudioChannel TestAudioChannelInAudioNodeStream(); diff --git a/dom/media/webaudio/test/test_mozaudiochannel.html b/dom/media/webaudio/test/test_mozaudiochannel.html index bfe19b33bb34..be6794432bea 100644 --- a/dom/media/webaudio/test/test_mozaudiochannel.html +++ b/dom/media/webaudio/test/test_mozaudiochannel.html @@ -18,27 +18,23 @@ function test_basic() { // Default is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); - // random wrong channel - ac.mozAudioChannelType = "foo"; - is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); - // Unpermitted channels - ac.mozAudioChannelType = "content"; + ac = new AudioContext("content"); is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); - ac.mozAudioChannelType = "notification"; + ac = new AudioContext("notification"); is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); - ac.mozAudioChannelType = "alarm"; + ac = new AudioContext("alarm"); is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); - ac.mozAudioChannelType = "telephony"; + ac = new AudioContext("telephony"); is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); - ac.mozAudioChannelType = "ringer"; + ac = new AudioContext("ringer"); is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); - ac.mozAudioChannelType = "publicnotification"; + ac = new AudioContext("publicnotification"); is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'"); runTest(); @@ -56,7 +52,7 @@ function test_permission(aChannel) { SpecialPowers.pushPermissions( [{ "type": "audio-channel-" + aChannel, "allow": true, "context": document }], function() { - ac.mozAudioChannelType = aChannel; + var ac = new AudioContext(aChannel); is(ac.mozAudioChannelType, aChannel, "Default ac channel == '" + aChannel + "'"); var channel = SpecialPowers.wrap(ac).testAudioChannelInAudioNodeStream(); diff --git a/dom/speakermanager/SpeakerManagerService.cpp b/dom/speakermanager/SpeakerManagerService.cpp index 48f998cdfb5e..5eabd704f534 100644 --- a/dom/speakermanager/SpeakerManagerService.cpp +++ b/dom/speakermanager/SpeakerManagerService.cpp @@ -99,15 +99,10 @@ SpeakerManagerService::TuruOnSpeaker(bool aOn) { nsCOMPtr audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID); NS_ENSURE_TRUE_VOID(audioManager); - int32_t phoneState; - audioManager->GetPhoneState(&phoneState); - int32_t forceuse = (phoneState == nsIAudioManager::PHONE_STATE_IN_CALL || - phoneState == nsIAudioManager::PHONE_STATE_IN_COMMUNICATION) - ? nsIAudioManager::USE_COMMUNICATION : nsIAudioManager::USE_MEDIA; if (aOn) { - audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_SPEAKER); + audioManager->SetForceForUse(nsIAudioManager::USE_MEDIA, nsIAudioManager::FORCE_SPEAKER); } else { - audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_NONE); + audioManager->SetForceForUse(nsIAudioManager::USE_MEDIA, nsIAudioManager::FORCE_NONE); } } diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl index bf51646abfee..6ad60b850a8b 100644 --- a/dom/webidl/AudioContext.webidl +++ b/dom/webidl/AudioContext.webidl @@ -78,8 +78,8 @@ interface AudioContext : EventTarget { // Mozilla extensions partial interface AudioContext { // Read AudioChannel.webidl for more information about this attribute. - [Pref="media.useAudioChannelService", SetterThrows] - attribute AudioChannel mozAudioChannelType; + [Pref="media.useAudioChannelService"] + readonly attribute AudioChannel mozAudioChannelType; // These 2 events are dispatched when the AudioContext object is muted by // the AudioChannelService. It's call 'interrupt' because when this event is diff --git a/editor/libeditor/nsHTMLDataTransfer.cpp b/editor/libeditor/nsHTMLDataTransfer.cpp index 381bba35dd20..85b630bd410f 100644 --- a/editor/libeditor/nsHTMLDataTransfer.cpp +++ b/editor/libeditor/nsHTMLDataTransfer.cpp @@ -655,6 +655,9 @@ nsHTMLEditor::DoInsertHTMLWithContext(const nsAString & aInputString, { tmp = selNode; selNode = GetNodeLocation(tmp, &selOffset); + // selNode might be null in case a mutation listener removed + // the stuff we just inserted from the DOM. + NS_ENSURE_STATE(selNode); ++selOffset; // want to be *after* last leaf node in paste } diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index 35c30f2a5242..6982ae8248b4 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -1270,7 +1270,7 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pa extents = ComputeGlyphsExtents(bboxes, &positions.front(), aBuffer.mNumGlyphs, 1.0f); ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(), &positions.front(), aBuffer.mNumGlyphs, cg); - delete bboxes; + delete[] bboxes; } else { CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs]; CGFontGetGlyphBBoxes(macFont->mFont, &glyphs.front(), aBuffer.mNumGlyphs, bboxes); @@ -1280,7 +1280,7 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pa CGContextSetFontSize(cg, macFont->mSize); CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), aBuffer.mNumGlyphs); - delete bboxes; + delete[] bboxes; } CGContextScaleCTM(cg, 1, -1); DrawGradient(mColorSpace, cg, aPattern, extents); diff --git a/ipc/ipdl/ipdl/builtin.py b/ipc/ipdl/ipdl/builtin.py index 24fea9660427..20898a39a625 100644 --- a/ipc/ipdl/ipdl/builtin.py +++ b/ipc/ipdl/ipdl/builtin.py @@ -46,7 +46,7 @@ HeaderIncludes = ( 'prtime.h', 'IPCMessageStart.h', 'ipc/IPCMessageUtils.h', - 'nsAutoPtr.h', + 'nsRefPtr.h', 'nsStringGlue.h', 'nsTArray.h', 'mozilla/ipc/ProtocolUtils.h', diff --git a/js/public/Class.h b/js/public/Class.h index 0c7b5063d629..50f5dd91c0c6 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -9,6 +9,7 @@ #ifndef js_Class_h #define js_Class_h +#include "mozilla/DebugOnly.h" #include "mozilla/NullPtr.h" #include "jstypes.h" @@ -226,9 +227,44 @@ typedef bool typedef bool (* UnwatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id); +class JS_FRIEND_API(ElementAdder) +{ + public: + enum GetBehavior { + // Check if the element exists before performing the Get and preserve + // holes. + CheckHasElemPreserveHoles, + + // Perform a Get operation, like obj[index] in JS. + GetElement + }; + + private: + // Only one of these is used. + JS::RootedObject resObj_; + JS::Value *vp_; + + uint32_t index_; + mozilla::DebugOnly length_; + GetBehavior getBehavior_; + + public: + ElementAdder(JSContext *cx, JSObject *obj, uint32_t length, GetBehavior behavior) + : resObj_(cx, obj), vp_(nullptr), index_(0), length_(length), getBehavior_(behavior) + {} + ElementAdder(JSContext *cx, JS::Value *vp, uint32_t length, GetBehavior behavior) + : resObj_(cx), vp_(vp), index_(0), length_(length), getBehavior_(behavior) + {} + + GetBehavior getBehavior() const { return getBehavior_; } + + void append(JSContext *cx, JS::HandleValue v); + void appendHole(); +}; + typedef bool -(* SliceOp)(JSContext *cx, JS::HandleObject obj, uint32_t begin, uint32_t end, - JS::HandleObject result); // result is actually preallocted. +(* GetElementsOp)(JSContext *cx, JS::HandleObject obj, uint32_t begin, uint32_t end, + ElementAdder *adder); // A generic type for functions mapping an object to another object, or null // if an error or exception was thrown on cx. @@ -364,7 +400,7 @@ struct ObjectOps DeleteGenericOp deleteGeneric; WatchOp watch; UnwatchOp unwatch; - SliceOp slice; // Optimized slice, can be null. + GetElementsOp getElements; JSNewEnumerateOp enumerate; ObjectOp thisObject; }; diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index ae86f8fb05c6..bdceba091af9 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -500,7 +500,7 @@ AsmJSHandleExecutionInterrupt() { AsmJSActivation *act = PerThreadData::innermostAsmJSActivation(); act->module().setInterrupted(true); - bool ret = CheckForInterrupt(act->cx()); + bool ret = HandleExecutionInterrupt(act->cx()); act->module().setInterrupted(false); return ret; } @@ -673,8 +673,8 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx) switch (kind) { case AsmJSImm_Runtime: return cx->runtimeAddressForJit(); - case AsmJSImm_RuntimeInterruptUint32: - return cx->runtimeAddressOfInterruptUint32(); + case AsmJSImm_RuntimeInterrupt: + return cx->runtimeAddressOfInterrupt(); case AsmJSImm_StackLimit: return cx->stackLimitAddressForJitCode(StackForUntrustedScript); case AsmJSImm_ReportOverRecursed: diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 5518697ba3a5..22baa228c083 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -2404,7 +2404,7 @@ LazyArrayBufferTable::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) TypedObject::obj_setGenericAttributes, \ TypedObject::obj_deleteGeneric, \ nullptr, nullptr, /* watch/unwatch */ \ - nullptr, /* slice */ \ + nullptr, /* getElements */ \ TypedObject::obj_enumerate, \ nullptr, /* thisObject */ \ } \ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 804e0f3c85c3..3ee0d1bc755a 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -133,6 +133,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, firstLine(lineNum), localsToFrameSlots_(sc->context), stackDepth(0), maxStackDepth(0), + yieldIndex(0), arrayCompDepth(0), emitLevel(0), constList(sc->context), @@ -2999,6 +3000,28 @@ BytecodeEmitter::isRunOnceLambda() !funbox->function()->name(); } +static bool +EmitYieldOp(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op) +{ + if (op == JSOP_FINALYIELDRVAL) + return Emit1(cx, bce, JSOP_FINALYIELDRVAL) >= 0; + + MOZ_ASSERT(op == JSOP_INITIALYIELD || op == JSOP_YIELD); + + ptrdiff_t off = EmitN(cx, bce, op, 3); + if (off < 0) + return false; + + if (bce->yieldIndex >= JS_BIT(24)) { + bce->reportError(nullptr, JSMSG_TOO_MANY_YIELDS); + return false; + } + + SET_UINT24(bce->code(off), bce->yieldIndex); + bce->yieldIndex++; + return true; +} + bool frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body) { @@ -3074,7 +3097,7 @@ frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNo return false; // No need to check for finally blocks, etc as in EmitReturn. - if (Emit1(cx, bce, JSOP_FINALYIELDRVAL) < 0) + if (!EmitYieldOp(cx, bce, JSOP_FINALYIELDRVAL)) return false; } @@ -5559,7 +5582,7 @@ EmitReturn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().dotGenerator, &sc)); if (!EmitAliasedVarOp(cx, JSOP_GETALIASEDVAR, sc, DontCheckLexical, bce)) return false; - if (Emit1(cx, bce, JSOP_FINALYIELDRVAL) < 0) + if (!EmitYieldOp(cx, bce, JSOP_FINALYIELDRVAL)) return false; } else if (top + static_cast(JSOP_RETURN_LENGTH) != bce->offset()) { bce->code()[top] = JSOP_SETRVAL; @@ -5598,7 +5621,7 @@ EmitYield(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (!EmitTree(cx, bce, pn->pn_right)) return false; - if (Emit1(cx, bce, pn->getOp()) < 0) + if (!EmitYieldOp(cx, bce, pn->getOp())) return false; if (pn->getOp() == JSOP_INITIALYIELD && Emit1(cx, bce, JSOP_POP) < 0) @@ -5643,7 +5666,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter, Parse return false; // Yield RESULT as-is, without re-boxing. - if (Emit1(cx, bce, JSOP_YIELD) < 0) // ITER RECEIVED + if (!EmitYieldOp(cx, bce, JSOP_YIELD)) // ITER RECEIVED return false; // Try epilogue. diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index ccc775404b8d..0043da13a5b2 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -126,6 +126,8 @@ struct BytecodeEmitter int32_t stackDepth; /* current stack depth in script frame */ uint32_t maxStackDepth; /* maximum stack depth so far */ + uint32_t yieldIndex; /* index stored as operand of yield ops */ + uint32_t arrayCompDepth; /* stack depth of array in comprehension */ unsigned emitLevel; /* js::frontend::EmitTree recursion level */ diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.cpp b/js/src/irregexp/NativeRegExpMacroAssembler.cpp index ea6a71ab3336..8cce4ee744b2 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp +++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp @@ -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->mainThread.jitStackLimit; masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok); // Exit with an exception. There is not enough space on the stack @@ -502,7 +502,7 @@ NativeRegExpMacroAssembler::Backtrack() // Check for an interrupt. Label noInterrupt; masm.branch32(Assembler::Equal, - AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0), + AbsoluteAddress(&runtime->interrupt), Imm32(0), &noInterrupt); masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0); masm.jump(&exit_label_); diff --git a/js/src/jit-test/tests/baseline/bug1095870.js b/js/src/jit-test/tests/baseline/bug1095870.js new file mode 100644 index 000000000000..e75cbbe5dc00 --- /dev/null +++ b/js/src/jit-test/tests/baseline/bug1095870.js @@ -0,0 +1,4 @@ +// |jit-test| ion-eager; +for (var j = 0; j < 2; j++) { + (false).__proto__ = 0 +} diff --git a/js/src/jit-test/tests/basic/function-apply-proxy.js b/js/src/jit-test/tests/basic/function-apply-proxy.js new file mode 100644 index 000000000000..9a00fdb3de7e --- /dev/null +++ b/js/src/jit-test/tests/basic/function-apply-proxy.js @@ -0,0 +1,26 @@ +// fun.apply(null, proxy) should not invoke the proxy's Has trap. + +var proxy = new Proxy({}, { + get: function (target, name, proxy) { + switch (name) { + case "length": + return 2; + case "0": + return 15; + case "1": + return undefined; + default: + assertEq(false, true); + } + }, + has: function (target, name) { + assertEq(false, true); + } +}); +function foo() { + assertEq(arguments.length, 2); + assertEq(arguments[0], 15); + assertEq(1 in arguments, true); + assertEq(arguments[1], undefined); +} +foo.apply(null, proxy); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 8491db46ea10..5f3cbc8cd0f8 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -30,6 +30,7 @@ using namespace js::jit; BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script) : BaselineCompilerSpecific(cx, alloc, script), + yieldOffsets_(cx), modifiesArguments_(false) { } @@ -186,7 +187,8 @@ BaselineCompiler::compile() icEntries_.length(), pcMappingIndexEntries.length(), pcEntries.length(), - bytecodeTypeMapEntries); + bytecodeTypeMapEntries, + yieldOffsets_.length()); if (!baselineScript) return Method_Error; @@ -243,6 +245,8 @@ BaselineCompiler::compile() // searches for the sought entry when queries are in linear order. bytecodeMap[script->nTypeSets()] = 0; + baselineScript->copyYieldEntries(script, yieldOffsets_); + if (script->compartment()->debugMode()) baselineScript->setDebugMode(); @@ -496,7 +500,7 @@ bool BaselineCompiler::emitStackCheck(bool earlyCheck) { Label skipCall; - void *limitAddr = cx->runtime()->mainThread.addressOfJitStackLimit(); + uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit; uint32_t slotsSize = script->nslots() * sizeof(Value); uint32_t tolerance = earlyCheck ? slotsSize : 0; @@ -646,7 +650,7 @@ BaselineCompiler::emitInterruptCheck() frame.syncStack(0); Label done; - void *interrupt = cx->runtimeAddressOfInterruptUint32(); + void *interrupt = (void *)&cx->runtime()->interrupt; masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done); prepareVMCall(); @@ -3235,3 +3239,118 @@ BaselineCompiler::emit_JSOP_REST() frame.push(R0); return true; } + +typedef JSObject *(*CreateGeneratorFn)(JSContext *, BaselineFrame *); +static const VMFunction CreateGeneratorInfo = FunctionInfo(jit::CreateGenerator); + +bool +BaselineCompiler::emit_JSOP_GENERATOR() +{ + MOZ_ASSERT(frame.stackDepth() == 0); + + masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + + prepareVMCall(); + pushArg(R0.scratchReg()); + if (!callVM(CreateGeneratorInfo)) + return false; + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); + frame.push(R0); + return true; +} + +bool +BaselineCompiler::addYieldOffset() +{ + MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD); + + uint32_t yieldIndex = GET_UINT24(pc); + + while (yieldIndex >= yieldOffsets_.length()) { + if (!yieldOffsets_.append(0)) + return false; + } + + static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH, + "code below assumes INITIALYIELD and YIELD have same length"); + yieldOffsets_[yieldIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH); + return true; +} + +typedef bool (*InitialSuspendFn)(JSContext *, HandleObject, BaselineFrame *, jsbytecode *); +static const VMFunction InitialSuspendInfo = FunctionInfo(jit::InitialSuspend); + +bool +BaselineCompiler::emit_JSOP_INITIALYIELD() +{ + if (!addYieldOffset()) + return false; + + frame.syncStack(0); + + // Store generator in R0, BaselineFrame pointer in R1. + masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); + masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg()); + + prepareVMCall(); + pushArg(ImmPtr(pc)); + pushArg(R1.scratchReg()); + pushArg(R0.scratchReg()); + + if (!callVM(InitialSuspendInfo)) + return false; + + masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), JSReturnOperand); + return emitReturn(); +} + +typedef bool (*NormalSuspendFn)(JSContext *, HandleObject, BaselineFrame *, jsbytecode *, uint32_t); +static const VMFunction NormalSuspendInfo = FunctionInfo(jit::NormalSuspend); + +bool +BaselineCompiler::emit_JSOP_YIELD() +{ + if (!addYieldOffset()) + return false; + + // Store generator in R0, BaselineFrame pointer in R1. + frame.popRegsAndSync(1); + masm.unboxObject(R0, R0.scratchReg()); + masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg()); + + prepareVMCall(); + pushArg(Imm32(frame.stackDepth())); + pushArg(ImmPtr(pc)); + pushArg(R1.scratchReg()); + pushArg(R0.scratchReg()); + + if (!callVM(NormalSuspendInfo)) + return false; + + masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), JSReturnOperand); + return emitReturn(); +} + +typedef bool (*FinalSuspendFn)(JSContext *, HandleObject, BaselineFrame *, jsbytecode *); +static const VMFunction FinalSuspendInfo = FunctionInfo(jit::FinalSuspend); + +bool +BaselineCompiler::emit_JSOP_FINALYIELDRVAL() +{ + // Store generator in R0, BaselineFrame pointer in R1. + frame.popRegsAndSync(1); + masm.unboxObject(R0, R0.scratchReg()); + masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg()); + + prepareVMCall(); + pushArg(ImmPtr(pc)); + pushArg(R1.scratchReg()); + pushArg(R0.scratchReg()); + + if (!callVM(FinalSuspendInfo)) + return false; + + masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); + return emitReturn(); +} diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index c6086465b023..baec72bfd2aa 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -173,6 +173,10 @@ namespace jit { _(JSOP_MOREITER) \ _(JSOP_ISNOITER) \ _(JSOP_ENDITER) \ + _(JSOP_GENERATOR) \ + _(JSOP_INITIALYIELD) \ + _(JSOP_YIELD) \ + _(JSOP_FINALYIELDRVAL) \ _(JSOP_CALLEE) \ _(JSOP_SETRVAL) \ _(JSOP_RETRVAL) \ @@ -197,6 +201,10 @@ class BaselineCompiler : public BaselineCompilerSpecific // equivalent positions when debug mode is off. CodeOffsetLabel postDebugPrologueOffset_; + // For each INITIALYIELD or YIELD op, this Vector maps the yield index + // to the bytecode offset of the next op. + Vector yieldOffsets_; + // Whether any on stack arguments are modified. bool modifiesArguments_; @@ -278,6 +286,8 @@ class BaselineCompiler : public BaselineCompilerSpecific bool addPCMappingEntry(bool addIndexEntry); + bool addYieldOffset(); + void getScopeCoordinateObject(Register reg); Address getScopeCoordinateAddressFromObject(Register objReg, Register reg); Address getScopeCoordinateAddress(Register reg); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index d0322d190fdc..497a45a2f3a7 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -7921,6 +7921,7 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_ // end up attaching a stub for the exact same access later. bool isTemporarilyUnoptimizable = false; if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS && + lhs.isObject() && !TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldShape, name, id, rhs, &attached, &isTemporarilyUnoptimizable)) { @@ -7963,6 +7964,7 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_ } if (!attached && + lhs.isObject() && !TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldShape, oldType, oldSlots, name, id, rhs, &attached)) { diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 3d0a3cc92b0a..36d3ab90ecbf 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -60,11 +60,6 @@ static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000; static bool CheckFrame(InterpreterFrame *fp) { - if (fp->script()->isGenerator()) { - JitSpew(JitSpew_BaselineAbort, "generator frame"); - return false; - } - if (fp->isDebuggerFrame()) { // Debugger eval-in-frame. These are likely short-running scripts so // don't bother compiling them for now. @@ -339,23 +334,26 @@ BaselineScript * BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset, uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset, size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize, - size_t bytecodeTypeMapEntries) + size_t bytecodeTypeMapEntries, size_t yieldEntries) { static const unsigned DataAlignment = sizeof(uintptr_t); size_t icEntriesSize = icEntries * sizeof(ICEntry); size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry); size_t bytecodeTypeMapSize = bytecodeTypeMapEntries * sizeof(uint32_t); + size_t yieldEntriesSize = yieldEntries * sizeof(uintptr_t); size_t paddedICEntriesSize = AlignBytes(icEntriesSize, DataAlignment); size_t paddedPCMappingIndexEntriesSize = AlignBytes(pcMappingIndexEntriesSize, DataAlignment); size_t paddedPCMappingSize = AlignBytes(pcMappingSize, DataAlignment); size_t paddedBytecodeTypesMapSize = AlignBytes(bytecodeTypeMapSize, DataAlignment); + size_t paddedYieldEntriesSize = AlignBytes(yieldEntriesSize, DataAlignment); size_t allocBytes = paddedICEntriesSize + paddedPCMappingIndexEntriesSize + paddedPCMappingSize + - paddedBytecodeTypesMapSize; + paddedBytecodeTypesMapSize + + paddedYieldEntriesSize; BaselineScript *script = jsscript->zone()->pod_malloc_with_extra(allocBytes); if (!script) @@ -379,7 +377,12 @@ BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilog offsetCursor += paddedPCMappingSize; script->bytecodeTypeMapOffset_ = bytecodeTypeMapEntries ? offsetCursor : 0; + offsetCursor += paddedBytecodeTypesMapSize; + script->yieldEntriesOffset_ = yieldEntries ? offsetCursor : 0; + offsetCursor += paddedYieldEntriesSize; + + MOZ_ASSERT(offsetCursor == sizeof(BaselineScript) + allocBytes); return script; } @@ -570,6 +573,17 @@ BaselineScript::icEntryFromReturnAddress(uint8_t *returnAddr) return icEntryFromReturnOffset(offset); } +void +BaselineScript::copyYieldEntries(JSScript *script, Vector &yieldOffsets) +{ + uint8_t **entries = yieldEntryList(); + + for (size_t i = 0; i < yieldOffsets.length(); i++) { + uint32_t offset = yieldOffsets[i]; + entries[i] = nativeCodeForPC(script, script->offsetToPC(offset)); + } +} + void BaselineScript::copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm) { diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 89243877a66b..8f62ad85b988 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -178,6 +178,10 @@ struct BaselineScript // they correspond to, for use by TypeScript::BytecodeTypes. uint32_t bytecodeTypeMapOffset_; + // For generator scripts, we store the native code address for each yield + // instruction. + uint32_t yieldEntriesOffset_; + public: // Do not call directly, use BaselineScript::New. This is public for cx->new_. BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, @@ -187,7 +191,8 @@ struct BaselineScript uint32_t epilogueOffset, uint32_t postDebugPrologueOffset, uint32_t spsPushToggleOffset, size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize, - size_t bytecodeTypeMapEntries); + size_t bytecodeTypeMapEntries, size_t yieldEntries); + static void Trace(JSTracer *trc, BaselineScript *script); static void Destroy(FreeOp *fop, BaselineScript *script); @@ -268,6 +273,9 @@ struct BaselineScript ICEntry *icEntryList() { return (ICEntry *)(reinterpret_cast(this) + icEntriesOffset_); } + uint8_t **yieldEntryList() { + return (uint8_t **)(reinterpret_cast(this) + yieldEntriesOffset_); + } PCMappingIndexEntry *pcMappingIndexEntryList() { return (PCMappingIndexEntry *)(reinterpret_cast(this) + pcMappingIndexOffset_); } @@ -319,6 +327,8 @@ struct BaselineScript void copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm); void adoptFallbackStubs(FallbackICStubSpace *stubSpace); + void copyYieldEntries(JSScript *script, Vector &yieldOffsets); + PCMappingIndexEntry &pcMappingIndexEntry(size_t index); CompactBufferReader pcMappingReader(size_t indexEntry); @@ -354,6 +364,9 @@ struct BaselineScript static size_t offsetOfFlags() { return offsetof(BaselineScript, flags_); } + static size_t offsetOfYieldEntriesOffset() { + return offsetof(BaselineScript, yieldEntriesOffset_); + } static void writeBarrierPre(Zone *zone, BaselineScript *script); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index be05db38ac57..b9b45e213d00 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3772,7 +3772,7 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir) Register tempReg = ToRegister(lir->getTempReg()); masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg); - masm.loadPtr(Address(tempReg, PerThreadData::offsetOfJitStackLimit()), tempReg); + masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg); // Conditional forward (unlikely) branch to failure. CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir); @@ -9950,7 +9950,7 @@ CodeGenerator::visitInterruptCheck(LInterruptCheck *lir) if (!ool) return false; - AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterruptUint32()); + AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterrupt()); masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry()); masm.bind(ool->rejoin()); return true; @@ -9960,8 +9960,8 @@ bool CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir) { Register scratch = ToRegister(lir->scratch()); - masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterruptUint32), scratch); - masm.load32(Address(scratch, 0), scratch); + masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch); + masm.load8ZeroExtend(Address(scratch, 0), scratch); Label rejoin; masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin); { diff --git a/js/src/jit/CompileWrappers.cpp b/js/src/jit/CompileWrappers.cpp index 210eaf97c563..0030b92b26d8 100644 --- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -43,7 +43,7 @@ CompileRuntime::addressOfJitTop() const void * CompileRuntime::addressOfJitStackLimit() { - return runtime()->mainThread.addressOfJitStackLimit(); + return &runtime()->mainThread.jitStackLimit; } const void * @@ -73,15 +73,15 @@ CompileRuntime::addressOfGCZeal() #endif const void * -CompileRuntime::addressOfInterruptUint32() +CompileRuntime::addressOfInterrupt() { - return runtime()->addressOfInterruptUint32(); + return &runtime()->interrupt; } const void * -CompileRuntime::addressOfInterruptParUint32() +CompileRuntime::addressOfInterruptPar() { - return runtime()->addressOfInterruptParUint32(); + return &runtime()->interruptPar; } const void * diff --git a/js/src/jit/CompileWrappers.h b/js/src/jit/CompileWrappers.h index 879bca9e8f45..fe896315d0ed 100644 --- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -50,8 +50,8 @@ class CompileRuntime const void *addressOfGCZeal(); #endif - const void *addressOfInterruptUint32(); - const void *addressOfInterruptParUint32(); + const void *addressOfInterrupt(); + const void *addressOfInterruptPar(); const void *addressOfThreadPool(); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 7dfeb7e52408..f6075bfa8246 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -428,7 +428,7 @@ JitRuntime::ensureIonCodeAccessible(JSRuntime *rt) ionCodeProtected_ = false; } - if (rt->hasPendingInterrupt()) { + if (rt->interrupt) { // The interrupt handler needs to be invoked by this thread, but we may // be inside a signal handler and have no idea what is above us on the // stack (probably we are executing Ion code at an arbitrary point, but @@ -1162,7 +1162,7 @@ IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code, // whether an interrupt is currently desired, matching the targets // established by ensureIonCodeAccessible() above. We don't handle the // interrupt immediately as the interrupt lock is held here. - if (cx->runtime()->hasPendingInterrupt()) + if (cx->runtime()->interrupt) PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck); else PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader); @@ -2720,6 +2720,7 @@ InvalidateActivation(FreeOp *fop, const JitActivationIterator &activations, bool JitSpew(JitSpew_IonInvalidate, "#%d rectifier frame @ %p", frameno, it.fp()); break; case JitFrame_Unwound_IonJS: + case JitFrame_Unwound_BaselineJS: case JitFrame_Unwound_BaselineStub: MOZ_CRASH("invalid"); case JitFrame_Unwound_Rectifier: diff --git a/js/src/jit/IonFrames-inl.h b/js/src/jit/IonFrames-inl.h index 47a0994fd480..f674984466c2 100644 --- a/js/src/jit/IonFrames-inl.h +++ b/js/src/jit/IonFrames-inl.h @@ -54,6 +54,7 @@ JitFrameIterator::isFakeExitFrame() const { bool res = (prevType() == JitFrame_Unwound_Rectifier || prevType() == JitFrame_Unwound_IonJS || + prevType() == JitFrame_Unwound_BaselineJS || prevType() == JitFrame_Unwound_BaselineStub || (prevType() == JitFrame_Entry && type() == JitFrame_Exit)); MOZ_ASSERT_IF(res, type() == JitFrame_Exit || type() == JitFrame_BaselineJS); diff --git a/js/src/jit/IonFrames.cpp b/js/src/jit/IonFrames.cpp index fbfc919e2f59..90faf61401d0 100644 --- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -280,6 +280,7 @@ SizeOfFramePrefix(FrameType type) case JitFrame_BaselineJS: case JitFrame_IonJS: case JitFrame_Bailout: + case JitFrame_Unwound_BaselineJS: case JitFrame_Unwound_IonJS: return IonJSFrameLayout::Size(); case JitFrame_BaselineStub: @@ -332,6 +333,8 @@ JitFrameIterator::operator++() type_ = current()->prevType(); if (type_ == JitFrame_Unwound_IonJS) type_ = JitFrame_IonJS; + else if (type_ == JitFrame_Unwound_BaselineJS) + type_ = JitFrame_BaselineJS; else if (type_ == JitFrame_Unwound_BaselineStub) type_ = JitFrame_BaselineStub; returnAddressToFp_ = current()->returnAddress(); @@ -808,6 +811,7 @@ void EnsureExitFrame(IonCommonFrameLayout *frame) { if (frame->prevType() == JitFrame_Unwound_IonJS || + frame->prevType() == JitFrame_Unwound_BaselineJS || frame->prevType() == JitFrame_Unwound_BaselineStub || frame->prevType() == JitFrame_Unwound_Rectifier) { @@ -835,6 +839,12 @@ EnsureExitFrame(IonCommonFrameLayout *frame) return; } + + if (frame->prevType() == JitFrame_BaselineJS) { + frame->changePrevType(JitFrame_Unwound_BaselineJS); + return; + } + MOZ_ASSERT(frame->prevType() == JitFrame_IonJS); frame->changePrevType(JitFrame_Unwound_IonJS); } @@ -1357,6 +1367,7 @@ MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations) MarkBailoutFrame(trc, frames); break; case JitFrame_Unwound_IonJS: + case JitFrame_Unwound_BaselineJS: MOZ_CRASH("invalid"); case JitFrame_Rectifier: MarkRectifierFrame(trc, frames); @@ -2506,6 +2517,7 @@ JitFrameIterator::dump() const fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize())); break; case JitFrame_Unwound_IonJS: + case JitFrame_Unwound_BaselineJS: fprintf(stderr, "Warning! Unwound JS frames are not observable.\n"); break; case JitFrame_Exit: diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index 086f28071807..11b8701b38fe 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -1238,7 +1238,7 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output) void MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail) { - movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptParUint32()), tempReg); + movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg); branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail); } diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h index f560969adab6..447c560fa7e5 100644 --- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -45,6 +45,7 @@ enum FrameType // An unwound JS frame is a JS frame signalling that its callee frame has been // turned into an exit frame (see EnsureExitFrame). Used by Ion bailouts and // Baseline exception unwinding. + JitFrame_Unwound_BaselineJS, JitFrame_Unwound_IonJS, // Like Unwound_IonJS, but the caller is a baseline stub frame. diff --git a/js/src/jit/ParallelFunctions.cpp b/js/src/jit/ParallelFunctions.cpp index 58df34db82cc..2080bb959ad4 100644 --- a/js/src/jit/ParallelFunctions.cpp +++ b/js/src/jit/ParallelFunctions.cpp @@ -147,7 +147,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx) } #endif - if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit(), &stackDummy_)) { + if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit, &stackDummy_)) { cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed); return false; } diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 164c7542d08e..bc798f6f1dda 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -113,17 +113,28 @@ NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap) bool CheckOverRecursed(JSContext *cx) { - // We just failed the jitStackLimit check. There are two possible reasons: - // - jitStackLimit was the real stack limit and we're over-recursed - // - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt - // and we need to call JSRuntime::handleInterrupt. + // IonMonkey's stackLimit is equal to nativeStackLimit by default. When we + // request an interrupt, we set the jitStackLimit to nullptr, which causes + // the stack limit check to fail. + // + // There are two states we're concerned about here: + // (1) The interrupt bit is set, and we need to fire the interrupt callback. + // (2) The stack limit has been exceeded, and we need to throw an error. + // + // Note that we can reach here if jitStackLimit is MAXADDR, but interrupt + // has not yet been set to 1. That's okay; it will be set to 1 very shortly, + // and in the interim we might just fire a few useless calls to + // CheckOverRecursed. #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false); #else JS_CHECK_RECURSION(cx, return false); #endif - gc::MaybeVerifyBarriers(cx); - return cx->runtime()->handleInterrupt(cx); + + if (cx->runtime()->interrupt) + return InterruptCheck(cx); + + return true; } // This function can get called in two contexts. In the usual context, it's @@ -167,8 +178,10 @@ CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame, JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false); #endif - gc::MaybeVerifyBarriers(cx); - return cx->runtime()->handleInterrupt(cx); + if (cx->runtime()->interrupt) + return InterruptCheck(cx); + + return true; } bool @@ -829,6 +842,57 @@ DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok) return ok; } +JSObject * +CreateGenerator(JSContext *cx, BaselineFrame *frame) +{ + return GeneratorObject::create(cx, frame); +} + +bool +InitialSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc) +{ + MOZ_ASSERT(*pc == JSOP_INITIALYIELD); + return GeneratorObject::initialSuspend(cx, obj, frame, pc); +} + +bool +NormalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc, + uint32_t stackDepth) +{ + MOZ_ASSERT(*pc == JSOP_YIELD); + + // Return value is still on the stack. + MOZ_ASSERT(stackDepth >= 1); + + // The expression stack slots are stored on the stack in reverse order, so + // we copy them to a Vector and pass a pointer to that instead. We use + // stackDepth - 1 because we don't want to include the return value. + AutoValueVector exprStack(cx); + if (!exprStack.reserve(stackDepth - 1)) + return false; + + size_t firstSlot = frame->numValueSlots() - stackDepth; + for (size_t i = 0; i < stackDepth - 1; i++) + exprStack.infallibleAppend(*frame->valueSlot(firstSlot + i)); + + MOZ_ASSERT(exprStack.length() == stackDepth - 1); + + return GeneratorObject::normalSuspend(cx, obj, frame, pc, exprStack.begin(), stackDepth - 1); +} + +bool +FinalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc) +{ + MOZ_ASSERT(*pc == JSOP_FINALYIELDRVAL); + + if (!GeneratorObject::finalSuspend(cx, obj)) { + // Leave this frame and propagate the exception to the caller. + return DebugEpilogue(cx, frame, pc, /* ok = */ false); + } + + return true; +} + bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame) { diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 6523e07cf57e..dbdeb996f743 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -706,6 +706,12 @@ bool DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mu bool DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok); bool DebugEpilogueOnBaselineReturn(JSContext *cx, BaselineFrame *frame, jsbytecode *pc); +JSObject *CreateGenerator(JSContext *cx, BaselineFrame *frame); +bool InitialSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc); +bool NormalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc, + uint32_t stackDepth); +bool FinalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc); + bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame); bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame); diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 9b54186df22e..2144df9b0be3 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -783,7 +783,7 @@ enum AsmJSImmKind AsmJSImm_PowD = AsmJSExit::Builtin_PowD, AsmJSImm_ATan2D = AsmJSExit::Builtin_ATan2D, AsmJSImm_Runtime, - AsmJSImm_RuntimeInterruptUint32, + AsmJSImm_RuntimeInterrupt, AsmJSImm_StackLimit, AsmJSImm_ReportOverRecursed, AsmJSImm_OnDetached, diff --git a/js/src/js.msg b/js/src/js.msg index abb167d27405..cb313742837f 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -303,6 +303,7 @@ MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 0, JSEXN_SYNTAXERR, "too many constructor MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 0, JSEXN_SYNTAXERR, "more than one switch default") MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 0, JSEXN_SYNTAXERR, "too many function arguments") MSG_DEF(JSMSG_TOO_MANY_LOCALS, 0, JSEXN_SYNTAXERR, "too many local variables") +MSG_DEF(JSMSG_TOO_MANY_YIELDS, 0, JSEXN_SYNTAXERR, "too many yield expressions") MSG_DEF(JSMSG_TOUGH_BREAK, 0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch") MSG_DEF(JSMSG_UNEXPECTED_TOKEN, 2, JSEXN_SYNTAXERR, "expected {0}, got {1}") MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index a913b3d7958a..0bec32dca66e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2031,48 +2031,68 @@ JS_GetExternalStringFinalizer(JSString *str) } static void -SetNativeStackQuotaAndLimit(JSRuntime *rt, StackKind kind, size_t stackSize) +SetNativeStackQuota(JSRuntime *rt, StackKind kind, size_t stackSize) { rt->nativeStackQuota[kind] = stackSize; + if (rt->nativeStackBase) + RecomputeStackLimit(rt, kind); +} +void +js::RecomputeStackLimit(JSRuntime *rt, StackKind kind) +{ + size_t stackSize = rt->nativeStackQuota[kind]; #if JS_STACK_GROWTH_DIRECTION > 0 if (stackSize == 0) { rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX; } else { MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize); - rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase + stackSize - 1; + rt->mainThread.nativeStackLimit[kind] = + rt->nativeStackBase + stackSize - 1; } #else if (stackSize == 0) { rt->mainThread.nativeStackLimit[kind] = 0; } else { MOZ_ASSERT(rt->nativeStackBase >= stackSize); - rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase - (stackSize - 1); + rt->mainThread.nativeStackLimit[kind] = + rt->nativeStackBase - (stackSize - 1); } #endif + + // If there's no pending interrupt request set on the runtime's main thread's + // jitStackLimit, then update it so that it reflects the new nativeStacklimit. + // + // 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 + // out of ion into the interpeter, which will do a proper recursion check. + if (kind == StackForUntrustedScript) { + JSRuntime::AutoLockForInterrupt lock(rt); + if (rt->mainThread.jitStackLimit != uintptr_t(-1)) { + rt->mainThread.jitStackLimit = rt->mainThread.nativeStackLimit[kind]; +#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) + rt->mainThread.jitStackLimit = jit::Simulator::StackLimit(); +#endif + } + } } JS_PUBLIC_API(void) -JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, size_t trustedScriptStackSize, +JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, + size_t trustedScriptStackSize, size_t untrustedScriptStackSize) { - MOZ_ASSERT(rt->requestDepth == 0); - + MOZ_ASSERT_IF(trustedScriptStackSize, + trustedScriptStackSize < systemCodeStackSize); if (!trustedScriptStackSize) trustedScriptStackSize = systemCodeStackSize; - else - MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize); - + MOZ_ASSERT_IF(untrustedScriptStackSize, + untrustedScriptStackSize < trustedScriptStackSize); if (!untrustedScriptStackSize) untrustedScriptStackSize = trustedScriptStackSize; - else - MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize); - - SetNativeStackQuotaAndLimit(rt, StackForSystemCode, systemCodeStackSize); - SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize); - SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize); - - rt->mainThread.initJitStackLimit(); + SetNativeStackQuota(rt, StackForSystemCode, systemCodeStackSize); + SetNativeStackQuota(rt, StackForTrustedScript, trustedScriptStackSize); + SetNativeStackQuota(rt, StackForUntrustedScript, untrustedScriptStackSize); } /************************************************************************/ diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 3946eec97cdc..0b434a3ac58a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2272,9 +2272,6 @@ JS_GetExternalStringFinalizer(JSString *str); * The stack quotas for each kind of code should be monotonically descending, * and may be specified with this function. If 0 is passed for a given kind * of code, it defaults to the value of the next-highest-priority kind. - * - * This function may only be called immediately after the runtime is initialized - * and before any code is executed and/or interrupts requested. */ extern JS_PUBLIC_API(void) JS_SetNativeStackQuota(JSRuntime *cx, size_t systemCodeStackSize, diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 46209b82bb4b..8fa6f8f034e6 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -237,12 +237,51 @@ GetElement(JSContext *cx, HandleObject obj, IndexType index, bool *hole, Mutable return GetElement(cx, obj, obj, index, hole, vp); } -static bool -GetElementsSlow(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp) +void +ElementAdder::append(JSContext *cx, HandleValue v) { - for (uint32_t i = 0; i < length; i++) { - if (!JSObject::getElement(cx, aobj, aobj, i, MutableHandleValue::fromMarkedLocation(&vp[i]))) - return false; + MOZ_ASSERT(index_ < length_); + if (resObj_) + resObj_->as().setDenseElementWithType(cx, index_++, v); + else + vp_[index_++] = v; +} + +void +ElementAdder::appendHole() +{ + MOZ_ASSERT(getBehavior_ == ElementAdder::CheckHasElemPreserveHoles); + MOZ_ASSERT(index_ < length_); + if (resObj_) { + MOZ_ASSERT(resObj_->as().getDenseElement(index_).isMagic(JS_ELEMENTS_HOLE)); + index_++; + } else { + vp_[index_++].setMagic(JS_ELEMENTS_HOLE); + } +} + +bool +js::GetElementsWithAdder(JSContext *cx, HandleObject obj, HandleObject receiver, + uint32_t begin, uint32_t end, ElementAdder *adder) +{ + MOZ_ASSERT(begin <= end); + + RootedValue val(cx); + for (uint32_t i = begin; i < end; i++) { + if (adder->getBehavior() == ElementAdder::CheckHasElemPreserveHoles) { + bool hole; + if (!GetElement(cx, obj, receiver, i, &hole, &val)) + return false; + if (hole) { + adder->appendHole(); + continue; + } + } else { + MOZ_ASSERT(adder->getBehavior() == ElementAdder::GetElement); + if (!JSObject::getElement(cx, obj, receiver, i, &val)) + return false; + } + adder->append(cx, val); } return true; @@ -272,7 +311,17 @@ js::GetElements(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp) } } - return GetElementsSlow(cx, aobj, length, vp); + if (js::GetElementsOp op = aobj->getOps()->getElements) { + ElementAdder adder(cx, vp, length, ElementAdder::GetElement); + return op(cx, aobj, 0, length, &adder); + } + + for (uint32_t i = 0; i < length; i++) { + if (!JSObject::getElement(cx, aobj, aobj, i, MutableHandleValue::fromMarkedLocation(&vp[i]))) + return false; + } + + return true; } /* @@ -2815,6 +2864,24 @@ GetIndexedPropertiesInRange(JSContext *cx, HandleObject obj, uint32_t begin, uin return true; } +static bool +SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver, + uint32_t begin, uint32_t end, HandleObject result) +{ + RootedValue value(cx); + for (uint32_t slot = begin; slot < end; slot++) { + bool hole; + if (!CheckForInterrupt(cx) || + !GetElement(cx, obj, receiver, slot, &hole, &value)) + { + return false; + } + if (!hole && !JSObject::defineElement(cx, result, slot - begin, value)) + return false; + } + return true; +} + static bool SliceSparse(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end, HandleObject result) { @@ -2913,14 +2980,16 @@ js::array_slice(JSContext *cx, unsigned argc, Value *vp) return false; TryReuseArrayType(obj, narr); - if (js::SliceOp op = obj->getOps()->slice) { - // Ensure that we have dense elements, so that DOM can use js::UnsafeDefineElement. + if (js::GetElementsOp op = obj->getOps()->getElements) { + // Ensure that we have dense elements, so that ElementAdder::append can + // use setDenseElementWithType. NativeObject::EnsureDenseResult result = narr->ensureDenseElements(cx, 0, end - begin); if (result == NativeObject::ED_FAILED) return false; if (result == NativeObject::ED_OK) { - if (!op(cx, obj, begin, end, narr)) + ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles); + if (!op(cx, obj, begin, end, &adder)) return false; args.rval().setObject(*narr); @@ -2943,24 +3012,6 @@ js::array_slice(JSContext *cx, unsigned argc, Value *vp) return true; } -JS_FRIEND_API(bool) -js::SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver, - uint32_t begin, uint32_t end, HandleObject result) -{ - RootedValue value(cx); - for (uint32_t slot = begin; slot < end; slot++) { - bool hole; - if (!CheckForInterrupt(cx) || - !GetElement(cx, obj, receiver, slot, &hole, &value)) - { - return false; - } - if (!hole && !JSObject::defineElement(cx, result, slot - begin, value)) - return false; - } - return true; -} - /* ES5 15.4.4.20. */ static bool array_filter(JSContext *cx, unsigned argc, Value *vp) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 412ae616d270..cb861567edfe 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -41,6 +41,7 @@ #include "gc/Marking.h" #include "jit/Ion.h" #include "js/CharacterEncoding.h" +#include "vm/Debugger.h" #include "vm/HelperThreads.h" #include "vm/Shape.h" @@ -970,6 +971,90 @@ js_GetErrorMessage(void *userRef, const unsigned errorNumber) return nullptr; } +bool +js::InvokeInterruptCallback(JSContext *cx) +{ + MOZ_ASSERT(cx->runtime()->requestDepth >= 1); + + JSRuntime *rt = cx->runtime(); + MOZ_ASSERT(rt->interrupt); + + // Reset the callback counter first, then run GC and yield. If another + // thread is racing us here we will accumulate another callback request + // which will be serviced at the next opportunity. + rt->interrupt = false; + + // IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt + // callbacks. + rt->resetJitStackLimit(); + + cx->gcIfNeeded(); + + rt->interruptPar = false; + + // A worker thread may have requested an interrupt after finishing an Ion + // compilation. + jit::AttachFinishedCompilations(cx); + + // Important: Additional callbacks can occur inside the callback handler + // if it re-enters the JS engine. The embedding must ensure that the + // callback is disconnected before attempting such re-entry. + JSInterruptCallback cb = cx->runtime()->interruptCallback; + if (!cb) + return true; + + if (cb(cx)) { + // Debugger treats invoking the interrupt callback as a "step", so + // invoke the onStep handler. + if (cx->compartment()->debugMode()) { + ScriptFrameIter iter(cx); + if (iter.script()->stepModeEnabled()) { + RootedValue rval(cx); + switch (Debugger::onSingleStep(cx, &rval)) { + case JSTRAP_ERROR: + return false; + case JSTRAP_CONTINUE: + return true; + case JSTRAP_RETURN: + // See note in Debugger::propagateForcedReturn. + Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval); + return false; + case JSTRAP_THROW: + cx->setPendingException(rval); + return false; + default:; + } + } + } + + return true; + } + + // No need to set aside any pending exception here: ComputeStackString + // already does that. + JSString *stack = ComputeStackString(cx); + JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr; + + const char16_t *chars; + AutoStableStringChars stableChars(cx); + if (flat && stableChars.initTwoByte(cx, flat)) + chars = stableChars.twoByteRange().start().get(); + else + chars = MOZ_UTF16("(stack not available)"); + JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, + JSMSG_TERMINATED, chars); + + return false; +} + +bool +js::HandleExecutionInterrupt(JSContext *cx) +{ + if (cx->runtime()->interrupt) + return InvokeInterruptCallback(cx); + return true; +} + ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind) : ContextFriendFields(rt), contextKind_(kind), diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4c81f3febbd0..3fa252c061a7 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -289,7 +289,7 @@ struct ThreadSafeContext : ContextFriendFields, PropertyName *emptyString() { return runtime_->emptyString; } FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); } void *runtimeAddressForJit() { return runtime_; } - void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); } + void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; } void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; } void *stackLimitAddressForJitCode(StackKind kind); size_t gcSystemPageSize() { return gc::SystemPageSize(); } @@ -782,15 +782,33 @@ extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; namespace js { +/* + * Invoke the interrupt callback and return false if the current execution + * is to be terminated. + */ +bool +InvokeInterruptCallback(JSContext *cx); + +bool +HandleExecutionInterrupt(JSContext *cx); + +/* + * Process any pending interrupt requests. Long-running inner loops in C++ must + * call this periodically to make sure they are interruptible --- that is, to + * make sure they do not prevent the slow script dialog from appearing. + * + * This can run a full GC or call the interrupt callback, which could do + * anything. In the browser, it displays the slow script dialog. + * + * If this returns true, the caller can continue; if false, the caller must + * break out of its loop. This happens if, for example, the user clicks "Stop + * script" on the slow script dialog; treat it as an uncatchable error. + */ MOZ_ALWAYS_INLINE bool CheckForInterrupt(JSContext *cx) { - // Add an inline fast-path since we have to check for interrupts in some hot - // C++ loops of library builtins. - JSRuntime *rt = cx->runtime(); - if (rt->hasPendingInterrupt()) - return rt->handleInterrupt(cx); - return true; + MOZ_ASSERT(cx->runtime()->requestDepth >= 1); + return !cx->runtime()->interrupt || InvokeInterruptCallback(cx); } /************************************************************************/ diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 08fe7d241e81..c89076c61e2d 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1386,14 +1386,6 @@ js::GetObjectMetadata(JSObject *obj) return obj->getMetadata(); } -JS_FRIEND_API(void) -js::UnsafeDefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value) -{ - MOZ_ASSERT(obj->isNative()); - MOZ_ASSERT(index < obj->as().getDenseInitializedLength()); - obj->as().setDenseElementWithType(cx, index, value); -} - JS_FRIEND_API(bool) js_DefineOwnProperty(JSContext *cx, JSObject *objArg, jsid idArg, JS::Handle descriptor, bool *bp) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index d7514cb5f7fb..c3a39c033fc2 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -323,7 +323,7 @@ namespace js { js::proxy_SetGenericAttributes, \ js::proxy_DeleteGeneric, \ js::proxy_Watch, js::proxy_Unwatch, \ - js::proxy_Slice, \ + js::proxy_GetElements, \ nullptr, /* enumerate */ \ nullptr, /* thisObject */ \ } \ @@ -411,8 +411,8 @@ proxy_Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObje extern JS_FRIEND_API(bool) proxy_Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id); extern JS_FRIEND_API(bool) -proxy_Slice(JSContext *cx, JS::HandleObject proxy, uint32_t begin, uint32_t end, - JS::HandleObject result); +proxy_GetElements(JSContext *cx, JS::HandleObject proxy, uint32_t begin, uint32_t end, + ElementAdder *adder); /* * A class of objects that return source code on demand. @@ -2578,12 +2578,9 @@ SetObjectMetadata(JSContext *cx, JS::HandleObject obj, JS::HandleObject metadata JS_FRIEND_API(JSObject *) GetObjectMetadata(JSObject *obj); -JS_FRIEND_API(void) -UnsafeDefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value); - JS_FRIEND_API(bool) -SliceSlowly(JSContext* cx, JS::HandleObject obj, JS::HandleObject receiver, - uint32_t begin, uint32_t end, JS::HandleObject result); +GetElementsWithAdder(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, + uint32_t begin, uint32_t end, js::ElementAdder *adder); JS_FRIEND_API(bool) ForwardToNative(JSContext *cx, JSNative native, const JS::CallArgs &args); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index bf4ec955c7fd..1921c6b8b776 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -526,7 +526,7 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind) rt->gc.runDebugGC(); #endif - if (rt->hasPendingInterrupt()) { + if (rt->interrupt) { // Invoking the interrupt callback can fail and we can't usefully // handle that here. Just check in case we need to collect instead. ncx->gcIfNeeded(); diff --git a/js/src/jsnativestack.h b/js/src/jsnativestack.h index bcfe0ad88d27..86abcd08ac5c 100644 --- a/js/src/jsnativestack.h +++ b/js/src/jsnativestack.h @@ -18,7 +18,6 @@ inline uintptr_t GetNativeStackBase() { uintptr_t stackBase = reinterpret_cast(GetNativeStackBaseImpl()); - MOZ_ASSERT(stackBase != 0); MOZ_ASSERT(stackBase % sizeof(void *) == 0); return stackBase; } diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 0b93fce23072..66e14fb6936f 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1067,8 +1067,7 @@ js_Disassemble1(JSContext *cx, HandleScript script, jsbytecode *pc, goto print_int; case JOF_UINT24: - MOZ_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY || op == JSOP_INITELEM_ARRAY || - op == JSOP_DUPAT); + MOZ_ASSERT(len == 4); i = (int)GET_UINT24(pc); goto print_int; diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index c9221fd0d5fb..2d122385609d 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -332,8 +332,8 @@ class JS_FRIEND_API(BaseProxyHandler) JS::HandleObject callable) const; virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) const; - virtual bool slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, - HandleObject result) const; + virtual bool getElements(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, + ElementAdder *adder) const; /* See comment for weakmapKeyDelegateOp in js/Class.h. */ virtual JSObject *weakmapKeyDelegate(JSObject *proxy) const; diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index ff0454427680..403e5f856f01 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -341,12 +341,12 @@ BaseProxyHandler::unwatch(JSContext *cx, HandleObject proxy, HandleId id) const } bool -BaseProxyHandler::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, - HandleObject result) const +BaseProxyHandler::getElements(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, + ElementAdder *adder) const { assertEnteredPolicy(cx, proxy, JSID_VOID, GET); - return js::SliceSlowly(cx, proxy, proxy, begin, end, result); + return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder); } bool diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 67d92da466de..505740fde96a 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -550,8 +550,8 @@ Proxy::unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) } /* static */ bool -Proxy::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, - HandleObject result) +Proxy::getElements(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, + ElementAdder *adder) { JS_CHECK_RECURSION(cx, return false); const BaseProxyHandler *handler = proxy->as().handler(); @@ -560,11 +560,11 @@ Proxy::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, if (!policy.allowed()) { if (policy.returnValue()) { MOZ_ASSERT(!cx->isExceptionPending()); - return js::SliceSlowly(cx, proxy, proxy, begin, end, result); + return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder); } return false; } - return handler->slice(cx, proxy, begin, end, result); + return handler->getElements(cx, proxy, begin, end, adder); } JSObject * @@ -835,10 +835,10 @@ js::proxy_Unwatch(JSContext *cx, HandleObject obj, HandleId id) } bool -js::proxy_Slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, - HandleObject result) +js::proxy_GetElements(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, + ElementAdder *adder) { - return Proxy::slice(cx, proxy, begin, end, result); + return Proxy::getElements(cx, proxy, begin, end, adder); } const Class js::ProxyObject::class_ = diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index f163c52cda67..c634a040a5fb 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -69,8 +69,8 @@ class Proxy static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable); static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id); - static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end, - HandleObject result); + static bool getElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end, + ElementAdder *adder); /* IC entry path for handling __noSuchMethod__ on access. */ static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id, diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index 7e1471311716..dd828fd771a4 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -1440,7 +1440,7 @@ ForkJoinShared::execute() // Sometimes a GC request occurs *just before* we enter into the // parallel section. Rather than enter into the parallel section // and then abort, we just check here and abort early. - if (cx_->runtime()->hasPendingInterruptPar()) + if (cx_->runtime()->interruptPar) return TP_RETRY_SEQUENTIALLY; AutoLockMonitor lock(*this); @@ -1518,7 +1518,7 @@ ForkJoinShared::executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit // Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the // lock has not been initialized in these cases. - thisThread.initJitStackLimitPar(stackLimit); + thisThread.jitStackLimit = stackLimit; executePortion(&thisThread, worker); TlsPerThreadData.set(nullptr); @@ -1551,7 +1551,7 @@ ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker) // // Thus, use GetNativeStackLimit instead of just propagating the // main thread's. - thisThread.initJitStackLimitPar(GetNativeStackLimit(cx_)); + thisThread.jitStackLimit = GetNativeStackLimit(cx_); executePortion(&thisThread, worker); TlsPerThreadData.set(oldData); @@ -1647,7 +1647,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke void ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx) { - MOZ_ASSERT(cx_->runtime()->hasPendingInterruptPar()); + MOZ_ASSERT(cx_->runtime()->interruptPar); // The GC Needed flag should not be set during parallel // execution. Instead, one of the requestGC() or // requestZoneGC() methods should be invoked. @@ -1826,7 +1826,7 @@ ForkJoinContext::hasAcquiredJSContext() const bool ForkJoinContext::check() { - if (runtime()->hasPendingInterruptPar()) { + if (runtime()->interruptPar) { shared_->setAbortFlagDueToInterrupt(*this); return false; } @@ -2273,6 +2273,13 @@ js::ParallelTestsShouldPass(JSContext *cx) cx->runtime()->gcZeal() == 0; } +void +js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode) +{ + if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon) + rt->interruptPar = true; +} + bool js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp) { diff --git a/js/src/vm/ForkJoin.h b/js/src/vm/ForkJoin.h index 8554d4f87f7d..55af6f09f7a1 100644 --- a/js/src/vm/ForkJoin.h +++ b/js/src/vm/ForkJoin.h @@ -546,6 +546,8 @@ bool InExclusiveParallelSection(); bool ParallelTestsShouldPass(JSContext *cx); +void RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode); + bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp); extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo; diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index dc27855d196f..4b65ac0f1014 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -60,19 +60,28 @@ GeneratorObject::create(JSContext *cx, AbstractFramePtr frame) bool GeneratorObject::suspend(JSContext *cx, HandleObject obj, AbstractFramePtr frame, jsbytecode *pc, - Value *vp, unsigned nvalues, GeneratorObject::SuspendKind suspendKind) + Value *vp, unsigned nvalues) { + MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD); + Rooted genObj(cx, &obj->as()); MOZ_ASSERT(!genObj->hasExpressionStack()); - if (suspendKind == NORMAL && genObj->isClosing()) { + if (*pc == JSOP_YIELD && genObj->isClosing()) { MOZ_ASSERT(genObj->is()); RootedValue val(cx, ObjectValue(*frame.callee())); js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, NullPtr()); return false; } - genObj->setSuspendedBytecodeOffset(pc - frame.script()->code(), suspendKind == INITIAL); + uint32_t yieldIndex = GET_UINT24(pc); + MOZ_ASSERT((*pc == JSOP_INITIALYIELD) == (yieldIndex == 0)); + + static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH, + "code below assumes INITIALYIELD and YIELD have same length"); + pc += JSOP_YIELD_LENGTH; + + genObj->setSuspendedBytecodeOffset(frame.script()->pcToOffset(pc), yieldIndex); genObj->setScopeChain(*frame.scopeChain()); if (nvalues) { @@ -128,6 +137,15 @@ GeneratorObject::resume(JSContext *cx, InterpreterActivation &activation, activation.regs().pc = callee->nonLazyScript()->code() + genObj->suspendedBytecodeOffset(); +#ifdef DEBUG + // Verify the YIELD_INDEX slot holds the right value. + static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH, + "code below assumes INITIALYIELD and YIELD have same length"); + jsbytecode *yieldpc = activation.regs().pc - JSOP_YIELD_LENGTH; + MOZ_ASSERT(*yieldpc == JSOP_INITIALYIELD || *yieldpc == JSOP_YIELD); + MOZ_ASSERT(GET_UINT24(yieldpc) == genObj->suspendedYieldIndex()); +#endif + // Always push on a value, even if we are raising an exception. In the // exception case, the stack needs to have something on it so that exception // handling doesn't skip the catch blocks. See TryNoteIter::settle. diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index e134591ceb3f..f8af42fe6324 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -28,12 +28,17 @@ class GeneratorObject : public NativeObject ARGS_OBJ_SLOT, EXPRESSION_STACK_SLOT, BYTECODE_OFFSET_SLOT, + YIELD_INDEX_SLOT, RESERVED_SLOTS }; - enum SuspendKind { INITIAL, NORMAL, FINAL }; enum ResumeKind { NEXT, THROW, CLOSE }; + private: + static bool suspend(JSContext *cx, HandleObject obj, AbstractFramePtr frame, jsbytecode *pc, + Value *vp, unsigned nvalues); + + public: static inline ResumeKind getResumeKind(jsbytecode *pc) { MOZ_ASSERT(*pc == JSOP_RESUME); unsigned arg = GET_UINT16(pc); @@ -52,19 +57,16 @@ class GeneratorObject : public NativeObject static JSObject *create(JSContext *cx, AbstractFramePtr frame); - static bool suspend(JSContext *cx, HandleObject obj, AbstractFramePtr frame, jsbytecode *pc, - Value *vp, unsigned nvalues, SuspendKind kind); - static bool resume(JSContext *cx, InterpreterActivation &activation, HandleObject obj, HandleValue arg, ResumeKind resumeKind); static bool initialSuspend(JSContext *cx, HandleObject obj, AbstractFramePtr frame, jsbytecode *pc) { - return suspend(cx, obj, frame, pc, nullptr, 0, INITIAL); + return suspend(cx, obj, frame, pc, nullptr, 0); } static bool normalSuspend(JSContext *cx, HandleObject obj, AbstractFramePtr frame, jsbytecode *pc, Value *vp, unsigned nvalues) { - return suspend(cx, obj, frame, pc, vp, nvalues, NORMAL); + return suspend(cx, obj, frame, pc, vp, nvalues); } static bool finalSuspend(JSContext *cx, HandleObject obj); @@ -149,11 +151,23 @@ class GeneratorObject : public NativeObject MOZ_ASSERT(isSuspended()); return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() >> 1; } - void setSuspendedBytecodeOffset(ptrdiff_t offset, bool newborn) { + void setSuspendedBytecodeOffset(ptrdiff_t offset, uint32_t yieldIndex) { + bool newborn = (yieldIndex == 0); MOZ_ASSERT(newborn ? getFixedSlot(BYTECODE_OFFSET_SLOT).isUndefined() : isRunning()); MOZ_ASSERT(offset > 0 && offset < MAX_BYTECODE_OFFSET); setFixedSlot(BYTECODE_OFFSET_SLOT, Int32Value((offset << 1) | (newborn ? 0x1 : 0))); MOZ_ASSERT(isSuspended()); + MOZ_ASSERT(yieldIndex <= INT32_MAX); + setFixedSlot(YIELD_INDEX_SLOT, Int32Value(yieldIndex)); + } + + // When the generator is suspended, the yield index slot contains the yield + // index (see JSOP_INITIALYIELD/JSOP_YIELD operand) of the yield instruction + // that suspended the generator. This is only used by JIT code to lookup the + // native code address when resuming. + uint32_t suspendedYieldIndex() const { + MOZ_ASSERT(isSuspended()); + return getFixedSlot(YIELD_INDEX_SLOT).toInt32(); } bool isClosed() const { return getFixedSlot(CALLEE_SLOT).isNull(); @@ -165,6 +179,7 @@ class GeneratorObject : public NativeObject setFixedSlot(ARGS_OBJ_SLOT, NullValue()); setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); setFixedSlot(BYTECODE_OFFSET_SLOT, NullValue()); + setFixedSlot(YIELD_INDEX_SLOT, NullValue()); } static size_t offsetOfCalleeSlot() { @@ -182,6 +197,9 @@ class GeneratorObject : public NativeObject static size_t offsetOfBytecodeOffsetSlot() { return getFixedSlotOffset(BYTECODE_OFFSET_SLOT); } + static size_t offsetOfYieldIndexSlot() { + return getFixedSlotOffset(YIELD_INDEX_SLOT); + } static size_t offsetOfExpressionStackSlot() { return getFixedSlotOffset(EXPRESSION_STACK_SLOT); } diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 281f7da76711..226590c0ee9d 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3362,7 +3362,7 @@ CASE(JSOP_INITIALYIELD) obj = ®S.sp[-1].toObject(); POP_RETURN_VALUE(); MOZ_ASSERT(REGS.stackDepth() == 0); - if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc + JSOP_INITIALYIELD_LENGTH)) + if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc)) goto error; goto successful_return_continuation; } @@ -3373,7 +3373,7 @@ CASE(JSOP_YIELD) MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); RootedObject &obj = rootObject0; obj = ®S.sp[-1].toObject(); - if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc + JSOP_YIELD_LENGTH, + if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc, REGS.spForStackDepth(0), REGS.stackDepth() - 2)) { goto error; diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 2268b8635224..78fada9960b6 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1565,19 +1565,19 @@ * interpretation. * Category: Statements * Type: Generator - * Operands: + * Operands: uint24_t yieldIndex * Stack: generator => */ \ - macro(JSOP_INITIALYIELD, 202,"initialyield", NULL, 1, 1, 1, JOF_BYTE) \ + macro(JSOP_INITIALYIELD, 202,"initialyield", NULL, 4, 1, 1, JOF_UINT24) \ /* * Pops the generator and the return value 'rval1', stops interpretation and * returns 'rval1'. Pushes sent value from 'send()' onto the stack. * Category: Statements * Type: Generator - * Operands: + * Operands: uint24_t yieldIndex * Stack: rval1, gen => rval2 */ \ - macro(JSOP_YIELD, 203,"yield", NULL, 1, 2, 1, JOF_BYTE) \ + macro(JSOP_YIELD, 203,"yield", NULL, 4, 2, 1, JOF_UINT24) \ /* * Pops the generator and suspends and closes it. Yields the value in the * frame's return value slot. diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 6429469b750e..6e2ac67120dc 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -621,8 +621,14 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, // in the bytecode interpreter, which can execute while tolerating // future interrupts. Otherwise, if we keep getting interrupted we // will never finish executing the regexp. - if (cx->runtime()->hasPendingInterrupt()) { - if (!cx->runtime()->handleInterrupt(cx)) + bool interrupted; + { + JSRuntime::AutoLockForInterrupt lock(cx->runtime()); + interrupted = cx->runtime()->interrupt; + } + + if (interrupted) { + if (!InvokeInterruptCallback(cx)) return RegExpRunStatus_Error; break; } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 44b5516380e5..105f3eccace9 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -36,7 +36,6 @@ #include "jit/PcScriptCache.h" #include "js/MemoryMetrics.h" #include "js/SliceBudget.h" -#include "vm/Debugger.h" #include "jscntxtinlines.h" #include "jsgcinlines.h" @@ -74,7 +73,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime) runtime_(runtime), jitTop(nullptr), jitJSContext(nullptr), - jitStackLimit_(0xbad), + jitStackLimit(0), #ifdef JS_TRACE_LOGGING traceLogger(nullptr), #endif @@ -136,8 +135,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) ), mainThread(this), parentRuntime(parentRuntime), - interrupt_(false), - interruptPar_(false), + interrupt(false), + interruptPar(false), handlingSignal(false), interruptCallback(nullptr), interruptLock(nullptr), @@ -156,7 +155,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) execAlloc_(nullptr), jitRuntime_(nullptr), selfHostingGlobal_(nullptr), - nativeStackBase(GetNativeStackBase()), + nativeStackBase(0), cxCallback(nullptr), destroyCompartmentCallback(nullptr), destroyZoneCallback(nullptr), @@ -322,6 +321,8 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) return false; #endif + nativeStackBase = GetNativeStackBase(); + jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint(); jitSupportsSimd = js::jit::JitSupportsSimd(); @@ -463,6 +464,17 @@ NewObjectCache::clearNurseryObjects(JSRuntime *rt) #endif } +void +JSRuntime::resetJitStackLimit() +{ + AutoLockForInterrupt lock(this); + mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]); + +#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) + mainThread.setJitStackLimit(js::jit::Simulator::StackLimit()); +#endif +} + void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes) { @@ -517,120 +529,33 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim #endif } -static bool -InvokeInterruptCallback(JSContext *cx) -{ - MOZ_ASSERT(cx->runtime()->requestDepth >= 1); - - cx->gcIfNeeded(); - - // A worker thread may have requested an interrupt after finishing an Ion - // compilation. - jit::AttachFinishedCompilations(cx); - - // Important: Additional callbacks can occur inside the callback handler - // if it re-enters the JS engine. The embedding must ensure that the - // callback is disconnected before attempting such re-entry. - JSInterruptCallback cb = cx->runtime()->interruptCallback; - if (!cb) - return true; - - if (cb(cx)) { - // Debugger treats invoking the interrupt callback as a "step", so - // invoke the onStep handler. - if (cx->compartment()->debugMode()) { - ScriptFrameIter iter(cx); - if (iter.script()->stepModeEnabled()) { - RootedValue rval(cx); - switch (Debugger::onSingleStep(cx, &rval)) { - case JSTRAP_ERROR: - return false; - case JSTRAP_CONTINUE: - return true; - case JSTRAP_RETURN: - // See note in Debugger::propagateForcedReturn. - Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval); - return false; - case JSTRAP_THROW: - cx->setPendingException(rval); - return false; - default:; - } - } - } - - return true; - } - - // No need to set aside any pending exception here: ComputeStackString - // already does that. - JSString *stack = ComputeStackString(cx); - JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr; - - const char16_t *chars; - AutoStableStringChars stableChars(cx); - if (flat && stableChars.initTwoByte(cx, flat)) - chars = stableChars.twoByteRange().start().get(); - else - chars = MOZ_UTF16("(stack not available)"); - JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, - JSMSG_TERMINATED, chars); - - return false; -} - -void -PerThreadData::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 - // out of ion into the interpeter, which will do a proper recursion check. -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - jitStackLimit_ = jit::Simulator::StackLimit(); -#else - jitStackLimit_ = nativeStackLimit[StackForUntrustedScript]; -#endif -} - -void -PerThreadData::initJitStackLimit() -{ - resetJitStackLimit(); -} - -void -PerThreadData::initJitStackLimitPar(uintptr_t limit) -{ - jitStackLimit_ = limit; -} - void JSRuntime::requestInterrupt(InterruptMode mode) { - interrupt_ = true; - interruptPar_ = true; - mainThread.jitStackLimit_ = UINTPTR_MAX; + AutoLockForInterrupt lock(this); + /* + * Invalidate ionTop to trigger its over-recursion check. Note this must be + * set before interrupt, to avoid racing with js::InvokeInterruptCallback, + * into a weird state where interrupt is stuck at 0 but jitStackLimit is + * MAXADDR. + */ + mainThread.setJitStackLimit(-1); + + interrupt = true; + + RequestInterruptForForkJoin(this, mode); + + /* + * asm.js and normal Ion code optionally use memory protection and signal + * handlers to halt running code. + */ if (canUseSignalHandlers()) { - AutoLockForInterrupt lock(this); RequestInterruptForAsmJSCode(this, mode); jit::RequestInterruptForIonCode(this, mode); } } -bool -JSRuntime::handleInterrupt(JSContext *cx) -{ - MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); - if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) { - interrupt_ = false; - interruptPar_ = false; - mainThread.resetJitStackLimit(); - return InvokeInterruptCallback(cx); - } - return true; -} - jit::ExecutableAllocator * JSRuntime::createExecutableAllocator(JSContext *cx) { diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 3735e3140fec..7c883a686db7 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -519,20 +519,13 @@ class PerThreadData : public PerThreadDataFriendFields */ JSContext *jitJSContext; - /* See comment for JSRuntime::interrupt_. */ - private: - mozilla::Atomic jitStackLimit_; - void resetJitStackLimit(); - friend struct ::JSRuntime; - public: - void initJitStackLimit(); - void initJitStackLimitPar(uintptr_t limit); + /* + * The stack limit checked by JIT code. This stack limit may be temporarily + * set to null to force JIT code to exit (e.g., for the operation callback). + */ + uintptr_t jitStackLimit; - uintptr_t jitStackLimit() const { return jitStackLimit_; } - - // For read-only JIT use: - void *addressOfJitStackLimit() { return &jitStackLimit_; } - static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); } + inline void setJitStackLimit(uintptr_t limit); // Information about the heap allocated backtrack stack used by RegExp JIT code. irregexp::RegExpStack regexpStack; @@ -685,6 +678,8 @@ class PerThreadData : public PerThreadDataFriendFields class AutoLockForExclusiveAccess; +void RecomputeStackLimit(JSRuntime *rt, StackKind kind); + } // namespace js struct JSRuntime : public JS::shadow::Runtime, @@ -708,56 +703,18 @@ struct JSRuntime : public JS::shadow::Runtime, */ JSRuntime *parentRuntime; - private: - mozilla::Atomic interrupt_; - mozilla::Atomic interruptPar_; - public: + /* + * If true, we've been asked to call the interrupt callback as soon as + * possible. + */ + mozilla::Atomic interrupt; - enum InterruptMode { - RequestInterruptMainThread, - RequestInterruptAnyThread, - RequestInterruptAnyThreadDontStopIon, - RequestInterruptAnyThreadForkJoin - }; - - // Any thread can call requestInterrupt() to request that the main JS thread - // stop running and call the interrupt callback (allowing the interrupt - // callback to halt execution). To stop the main JS thread, requestInterrupt - // sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to - // UINTPTR_MAX). The JS engine must continually poll one of these fields - // and call handleInterrupt if either field has the interrupt value. (The - // point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already - // needs to guard on jitStackLimit_ in every function prologue to avoid - // stack overflow, so we avoid a second branch on interrupt_ by setting - // jitStackLimit_ to a value that is guaranteed to fail the guard.) - // - // Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed - // Atomic so, while the writes are guaranteed to eventually be visible to - // the main thread, it can happen in any order. handleInterrupt calls the - // interrupt callback if either is set, so it really doesn't matter as long - // as the JS engine is continually polling at least one field. In corner - // cases, this relaxed ordering could lead to an interrupt handler being - // called twice in succession after a single requestInterrupt call, but - // that's fine. - void requestInterrupt(InterruptMode mode); - bool handleInterrupt(JSContext *cx); - - MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const { - return interrupt_; - } - MOZ_ALWAYS_INLINE bool hasPendingInterruptPar() const { - return interruptPar_; - } - - // For read-only JIT use: - void *addressOfInterruptUint32() { - static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers"); - return &interrupt_; - } - void *addressOfInterruptParUint32() { - static_assert(sizeof(interruptPar_) == sizeof(uint32_t), "Assumed by JIT callers"); - return &interruptPar_; - } + /* + * If non-zero, ForkJoin should service an interrupt. This is a separate + * flag from |interrupt| because we cannot use the mprotect trick with PJS + * code and ignore the TriggerCallbackAnyThreadDontStopIon trigger. + */ + mozilla::Atomic interruptPar; /* Set when handling a signal for a thread associated with this runtime. */ bool handlingSignal; @@ -943,7 +900,7 @@ struct JSRuntime : public JS::shadow::Runtime, void setDefaultVersion(JSVersion v) { defaultVersion_ = v; } /* Base address of the native stack for the current thread. */ - const uintptr_t nativeStackBase; + uintptr_t nativeStackBase; /* The native stack size limit that runtime should not exceed. */ size_t nativeStackQuota[js::StackKindCount]; @@ -1309,6 +1266,10 @@ struct JSRuntime : public JS::shadow::Runtime, bool jitSupportsFloatingPoint; bool jitSupportsSimd; + // Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1) + // has been noticed by Ion/Baseline. + void resetJitStackLimit(); + // Cache for jit::GetPcScript(). js::jit::PcScriptCache *ionPcScriptCache; @@ -1369,6 +1330,17 @@ struct JSRuntime : public JS::shadow::Runtime, /* onOutOfMemory but can call the largeAllocationFailureCallback. */ JS_FRIEND_API(void *) onOutOfMemoryCanGC(void *p, size_t bytes); + // Ways in which the interrupt callback on the runtime can be triggered, + // varying based on which thread is triggering the callback. + enum InterruptMode { + RequestInterruptMainThread, + RequestInterruptAnyThread, + RequestInterruptAnyThreadDontStopIon, + RequestInterruptAnyThreadForkJoin + }; + + void requestInterrupt(InterruptMode mode); + void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime); private: @@ -1592,6 +1564,13 @@ class MOZ_STACK_CLASS AutoKeepAtoms } }; +inline void +PerThreadData::setJitStackLimit(uintptr_t limit) +{ + MOZ_ASSERT(runtime_->currentThreadOwnsInterruptLock()); + jitStackLimit = limit; +} + inline JSRuntime * PerThreadData::runtimeFromMainThread() { diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index f4fdfc2b21e6..1c6ea12f9b8f 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -605,7 +605,7 @@ const Class DynamicWithObject::class_ = { with_SetGenericAttributes, with_DeleteGeneric, nullptr, nullptr, /* watch/unwatch */ - nullptr, /* slice */ + nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ with_ThisObject, } @@ -1045,7 +1045,7 @@ const Class UninitializedLexicalObject::class_ = { uninitialized_SetGenericAttributes, uninitialized_DeleteGeneric, nullptr, nullptr, /* watch/unwatch */ - nullptr, /* slice */ + nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ nullptr, /* this */ } diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 47d9c755a8e3..d45bfde6576a 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -34,7 +34,7 @@ namespace js { * Nightly) and without (all others). FIXME: Bug 1066322 - Enable ES6 symbols * in all builds. */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 194; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 196; static_assert(XDR_BYTECODE_VERSION_SUBTRAHEND % 2 == 0, "see the comment above"); static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - (XDR_BYTECODE_VERSION_SUBTRAHEND diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index 762747134a2d..471c27d86dd6 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -697,7 +697,7 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = { nullptr, // setGenericAttributes nullptr, // deleteGeneric nullptr, nullptr, // watch/unwatch - nullptr, // slice + nullptr, // getElements XPC_WN_JSOp_Enumerate, XPC_WN_JSOp_ThisObject, } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index f4c729c2bf83..f73634d612c1 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -974,7 +974,7 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS::HandleObject obj); nullptr, /* setGenericAttributes */ \ nullptr, /* deleteGeneric */ \ nullptr, nullptr, /* watch/unwatch */ \ - nullptr, /* slice */ \ + nullptr, /* getElements */ \ XPC_WN_JSOp_Enumerate, \ XPC_WN_JSOp_ThisObject, \ } @@ -997,7 +997,7 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS::HandleObject obj); nullptr, /* setGenericAttributes */ \ nullptr, /* deleteGeneric */ \ nullptr, nullptr, /* watch/unwatch */ \ - nullptr, /* slice */ \ + nullptr, /* getElements */ \ XPC_WN_JSOp_Enumerate, \ XPC_WN_JSOp_ThisObject, \ } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 8e8cc0870649..ac8193b28937 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -8832,7 +8832,7 @@ PresShell::IsVisible() // inner view of subdoc frame view = view->GetParent(); if (!view) - return true; + return mIsActive; // subdoc view view = view->GetParent(); diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index b12759c225a6..48d524a2ee94 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -318,7 +318,7 @@ nsFieldSetFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { LogicalSize result = nsContainerFrame::ComputeSize(aRenderingContext, aWM, diff --git a/layout/forms/nsFieldSetFrame.h b/layout/forms/nsFieldSetFrame.h index 626ce58f45b7..768f097eee0a 100644 --- a/layout/forms/nsFieldSetFrame.h +++ b/layout/forms/nsFieldSetFrame.h @@ -29,7 +29,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const MOZ_OVERRIDE; /** diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index b35e67482b31..4e1658be70cf 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -641,7 +641,7 @@ FloatMarginISize(const nsHTMLReflowState& aCBReflowState, aFloatOffsetState.ComputedLogicalBorderPadding().Size(wm) - aFloatOffsetState.ComputedLogicalPadding().Size(wm), aFloatOffsetState.ComputedLogicalPadding().Size(wm), - true); + nsIFrame::ComputeSizeFlags::eShrinkWrap); return floatSize.ISize(wm) + aFloatOffsetState.ComputedLogicalMargin().IStartEnd(wm) + diff --git a/layout/generic/nsFirstLetterFrame.cpp b/layout/generic/nsFirstLetterFrame.cpp index e1a3c5251158..95c813168847 100644 --- a/layout/generic/nsFirstLetterFrame.cpp +++ b/layout/generic/nsFirstLetterFrame.cpp @@ -147,7 +147,7 @@ nsFirstLetterFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { if (GetPrevInFlow()) { // We're wrapping the text *after* the first letter, so behave like an diff --git a/layout/generic/nsFirstLetterFrame.h b/layout/generic/nsFirstLetterFrame.h index 8295a428fb93..269257d613a1 100644 --- a/layout/generic/nsFirstLetterFrame.h +++ b/layout/generic/nsFirstLetterFrame.h @@ -58,7 +58,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual void Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 2cc1a5ac4d2d..9670a0ae808b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4074,12 +4074,12 @@ nsFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { LogicalSize result = ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, - aFlags & eShrinkWrap); + aFlags & ComputeSizeFlags::eShrinkWrap); LogicalSize boxSizingAdjust(aWM); const nsStylePosition *stylePos = StylePosition(); @@ -8399,7 +8399,7 @@ nsFrame::BoxReflow(nsBoxLayoutState& aState, reflowState.ComputedLogicalBorderPadding().Size(wm) - reflowState.ComputedLogicalPadding().Size(wm), reflowState.ComputedLogicalPadding().Size(wm), - false).Height(wm)); + ComputeSizeFlags::eDefault).Height(wm)); } } diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 0f69001cb442..74d5dc820e90 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -257,7 +257,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; // Compute tight bounds assuming this frame honours its border, background // and outline, its children's tight bounds, and nothing else. diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index 62158e04f4e0..11ac7899fd6f 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -161,7 +161,7 @@ nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { nsIntSize size = GetCanvasSize(); diff --git a/layout/generic/nsHTMLCanvasFrame.h b/layout/generic/nsHTMLCanvasFrame.h index 4351f53effb6..612087cf728b 100644 --- a/layout/generic/nsHTMLCanvasFrame.h +++ b/layout/generic/nsHTMLCanvasFrame.h @@ -68,7 +68,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual void Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index cc334691e55e..dbb11cf73026 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -1461,9 +1461,11 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, bool widthIsAuto = eStyleUnit_Auto == mStylePosition->mWidth.GetUnit(); bool heightIsAuto = eStyleUnit_Auto == mStylePosition->mHeight.GetUnit(); - uint32_t computeSizeFlags = 0; + typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags; + ComputeSizeFlags computeSizeFlags = ComputeSizeFlags::eDefault; if (leftIsAuto || rightIsAuto) { - computeSizeFlags |= nsIFrame::eShrinkWrap; + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); } { @@ -2117,7 +2119,9 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, AutoMaybeDisableFontInflation an(frame); bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType); - uint32_t computeSizeFlags = isBlock ? 0 : nsIFrame::eShrinkWrap; + typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags; + ComputeSizeFlags computeSizeFlags = + isBlock ? ComputeSizeFlags::eDefault : ComputeSizeFlags::eShrinkWrap; // Make sure legend frames with display:block and width:auto still // shrink-wrap. @@ -2126,17 +2130,20 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || (aFrameType == nsGkAtoms::scrollFrame && frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame))) { - computeSizeFlags |= nsIFrame::eShrinkWrap; + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); } const nsFlexContainerFrame* flexContainerFrame = GetFlexContainer(frame); if (flexContainerFrame) { - computeSizeFlags |= nsIFrame::eShrinkWrap; + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); // If we're inside of a flex container that needs to measure our // auto height, pass that information along to ComputeSize(). if (mFlags.mIsFlexContainerMeasuringHeight) { - computeSizeFlags |= nsIFrame::eUseAutoHeight; + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoHeight); } } else { MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 3bf6d18d68b3..f76fc07f141d 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1634,7 +1634,8 @@ public: /** * Bit-flags to pass to ComputeSize in |aFlags| parameter. */ - enum { + enum ComputeSizeFlags { + eDefault = 0, /* Set if the frame is in a context where non-replaced blocks should * shrink-wrap (e.g., it's floating, absolutely positioned, or * inline-block). */ @@ -1693,7 +1694,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) = 0; + ComputeSizeFlags aFlags) = 0; /** * Compute a tight bounding rectangle for the frame. This is a rectangle diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 153fceb3e57d..b3d4e615508d 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -106,16 +106,17 @@ static bool HaveFixedSize(const nsStylePosition* aStylePosition) return aStylePosition->mWidth.IsCoordPercentCalcUnit() && aStylePosition->mHeight.IsCoordPercentCalcUnit(); } + // use the data in the reflow state to decide if the image has a constrained size -// (i.e. width and height that are based on the containing block size and not the image size) +// (i.e. width and height that are based on the containing block size and not the image size) // so we can avoid animated GIF related reflows inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState) -{ +{ NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition"); - // when an image has percent css style height or width, but ComputedHeight() - // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE + // when an image has percent css style height or width, but ComputedHeight() + // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE // it needs to return false to cause an incremental reflow later - // if an image is inside table like bug 156731 simple testcase III, + // if an image is inside table like bug 156731 simple testcase III, // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false // see bug 156731 @@ -127,7 +128,7 @@ inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState) (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() || 0 == aReflowState.ComputedWidth()))) ? false - : HaveFixedSize(aReflowState.mStylePosition); + : HaveFixedSize(aReflowState.mStylePosition); } nsIFrame* @@ -773,7 +774,7 @@ nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { EnsureIntrinsicSizeAndRatio(); diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index 2b1299498b2b..9eb1eb1ca3dd 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -184,7 +184,7 @@ protected: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; bool IsServerImageMap(); diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index 425262877438..3e31510b4f1e 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -233,7 +233,7 @@ nsInlineFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { // Inlines and text don't compute size before reflow. return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); diff --git a/layout/generic/nsInlineFrame.h b/layout/generic/nsInlineFrame.h index 5d19be72fd81..fd59f28a3e91 100644 --- a/layout/generic/nsInlineFrame.h +++ b/layout/generic/nsInlineFrame.h @@ -78,7 +78,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual nsRect ComputeTightBounds(gfxContext* aContext) const MOZ_OVERRIDE; virtual void Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index cacf3d895042..2d242fe95bcd 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -712,7 +712,7 @@ nsSubDocumentFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); if (subDocRoot) { diff --git a/layout/generic/nsSubDocumentFrame.h b/layout/generic/nsSubDocumentFrame.h index da1c10b82454..c4414a32e6ca 100644 --- a/layout/generic/nsSubDocumentFrame.h +++ b/layout/generic/nsSubDocumentFrame.h @@ -69,7 +69,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual void Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 35903ac26947..aac55f276729 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -7553,7 +7553,7 @@ nsTextFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { // Inlines and text don't compute size before reflow. return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index fb9ffb46bcaa..16c0560dbc9b 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -218,7 +218,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual nsRect ComputeTightBounds(gfxContext* aContext) const MOZ_OVERRIDE; virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext, nscoord* aX, diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index 88ca14bc90ba..06e6a767d2a6 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -489,7 +489,7 @@ nsVideoFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { nsSize size = GetVideoIntrinsicSize(aRenderingContext); diff --git a/layout/generic/nsVideoFrame.h b/layout/generic/nsVideoFrame.h index 707713bfdbe0..22e53e3ed8f9 100644 --- a/layout/generic/nsVideoFrame.h +++ b/layout/generic/nsVideoFrame.h @@ -58,7 +58,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; diff --git a/layout/style/test/test_value_storage.html b/layout/style/test/test_value_storage.html index 46985c8c8d4d..494e76f1273c 100644 --- a/layout/style/test/test_value_storage.html +++ b/layout/style/test/test_value_storage.html @@ -314,7 +314,7 @@ function runTest() { } SimpleTest.waitForExplicitFinish(); -SimpleTest.requestLongerTimeout(3); +SimpleTest.requestLongerTimeout(4); SpecialPowers.pushPrefEnv({ set: [["layout.css.variables.enabled", true]] }, runTest); diff --git a/layout/svg/nsSVGOuterSVGFrame.cpp b/layout/svg/nsSVGOuterSVGFrame.cpp index b9298167be61..c4a25a2b5733 100644 --- a/layout/svg/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/nsSVGOuterSVGFrame.cpp @@ -271,7 +271,7 @@ nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) { // The embedding element has sized itself using the CSS replaced element diff --git a/layout/svg/nsSVGOuterSVGFrame.h b/layout/svg/nsSVGOuterSVGFrame.h index 07203c54674d..a77577cd54a8 100644 --- a/layout/svg/nsSVGOuterSVGFrame.h +++ b/layout/svg/nsSVGOuterSVGFrame.h @@ -53,7 +53,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual void Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 74e563c22c9c..9e56b22cf862 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1562,7 +1562,7 @@ nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, - uint32_t aFlags) + ComputeSizeFlags aFlags) { LogicalSize result = nsContainerFrame::ComputeSize(aRenderingContext, aWM, diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index da918d32d8a5..62562501efd6 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -311,7 +311,7 @@ public: const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, const mozilla::LogicalSize& aPadding, - uint32_t aFlags) MOZ_OVERRIDE; + ComputeSizeFlags aFlags) MOZ_OVERRIDE; virtual mozilla::LogicalSize ComputeAutoSize(nsRenderingContext *aRenderingContext, diff --git a/layout/tables/nsTableOuterFrame.cpp b/layout/tables/nsTableOuterFrame.cpp index 4cb29163a285..271da76ff930 100644 --- a/layout/tables/nsTableOuterFrame.cpp +++ b/layout/tables/nsTableOuterFrame.cpp @@ -493,7 +493,7 @@ ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext, offsets.ComputedLogicalBorderPadding().Size(wm) - offsets.ComputedLogicalPadding().Size(wm), offsets.ComputedLogicalPadding().Size(wm), - true); + nsIFrame::ComputeSizeFlags::eShrinkWrap); if (aMarginResult) *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(wm); return size.ISize(wm) + offsets.ComputedLogicalMargin().IStartEnd(wm) + diff --git a/security/manager/locales/en-US/chrome/pippki/pippki.properties b/security/manager/locales/en-US/chrome/pippki/pippki.properties index 23f0be0dd8d9..85c29536ed65 100644 --- a/security/manager/locales/en-US/chrome/pippki/pippki.properties +++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties @@ -147,17 +147,17 @@ writeFileUnknownError=Unknown error addExceptionBrandedWarning2=You are about to override how %S identifies this site. addExceptionInvalidHeader=This site attempts to identify itself with invalid information. addExceptionDomainMismatchShort=Wrong Site -addExceptionDomainMismatchLong=Certificate belongs to a different site, which could indicate an identity theft. +addExceptionDomainMismatchLong2=The certificate belongs to a different site, which could mean that someone is trying to impersonate this site. addExceptionExpiredShort=Outdated Information -addExceptionExpiredLong=Certificate is not currently valid. It is impossible to verify whether this identity was reported as stolen or lost. +addExceptionExpiredLong2=The certificate is not currently valid. It may have been stolen or lost, and could be used by someone to impersonate this site. addExceptionUnverifiedOrBadSignatureShort=Unknown Identity -addExceptionUnverifiedOrBadSignatureLong=Certificate is not trusted, because it hasn't been verified by a recognized authority using a secure signature. +addExceptionUnverifiedOrBadSignatureLong2=The certificate is not trusted because it hasn't been verified as issued by a trusted authority using a secure signature. addExceptionValidShort=Valid Certificate addExceptionValidLong=This site provides valid, verified identification. There is no need to add an exception. addExceptionCheckingShort=Checking Information -addExceptionCheckingLong=Attempting to identify the site… +addExceptionCheckingLong2=Attempting to identify this site… addExceptionNoCertShort=No Information Available -addExceptionNoCertLong=Unable to obtain identification status for the given site. +addExceptionNoCertLong2=Unable to obtain identification status for this site. addExceptionConnectionFailed=Connection Failed #Certificate Exists in database diff --git a/security/manager/pki/resources/content/exceptionDialog.js b/security/manager/pki/resources/content/exceptionDialog.js index 229500b0fe09..640791f898fc 100644 --- a/security/manager/pki/resources/content/exceptionDialog.js +++ b/security/manager/pki/resources/content/exceptionDialog.js @@ -187,11 +187,11 @@ function updateCertStatus() { if(gCert) { if(gBroken) { var mms = "addExceptionDomainMismatchShort"; - var mml = "addExceptionDomainMismatchLong"; + var mml = "addExceptionDomainMismatchLong2"; var exs = "addExceptionExpiredShort"; - var exl = "addExceptionExpiredLong"; + var exl = "addExceptionExpiredLong2"; var uts = "addExceptionUnverifiedOrBadSignatureShort"; - var utl = "addExceptionUnverifiedOrBadSignatureLong"; + var utl = "addExceptionUnverifiedOrBadSignatureLong2"; var use1 = false; if (gSSLStatus.isDomainMismatch) { bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_DOMAIN; @@ -263,7 +263,7 @@ function updateCertStatus() { } else if (gChecking) { shortDesc = "addExceptionCheckingShort"; - longDesc = "addExceptionCheckingLong"; + longDesc = "addExceptionCheckingLong2"; // We're checking the certificate, so we disable the Get Certificate // button to make sure that the user can't interrupt the process and // trigger another certificate fetch. @@ -274,7 +274,7 @@ function updateCertStatus() { } else { shortDesc = "addExceptionNoCertShort"; - longDesc = "addExceptionNoCertLong"; + longDesc = "addExceptionNoCertLong2"; // We're done checking the certificate, so allow the user to check it again. document.getElementById("checkCertButton").disabled = false; document.getElementById("viewCertButton").disabled = true; diff --git a/testing/marionette/client/marionette/marionette.py b/testing/marionette/client/marionette/marionette.py index d1b71c61b142..66458dd08ab6 100644 --- a/testing/marionette/client/marionette/marionette.py +++ b/testing/marionette/client/marionette/marionette.py @@ -479,6 +479,7 @@ class Marionette(object): self.bin = bin self.instance = None self.session = None + self.session_id = None self.window = None self.runner = None self.emulator = None @@ -607,12 +608,12 @@ class Marionette(object): @do_crash_check def _send_message(self, command, response_key="ok", **kwargs): - if not self.session and command != "newSession": + if not self.session_id and command != "newSession": raise errors.MarionetteException("Please start a session") message = {"name": command} - if self.session: - message["sessionId"] = self.session + if self.session_id: + message["sessionId"] = self.session_id if kwargs: message["parameters"] = kwargs @@ -637,6 +638,8 @@ class Marionette(object): continue; break; + if not self.session_id: + self.session_id = response.get("sessionId", None) if response_key in response: return response[response_key] @@ -811,7 +814,7 @@ class Marionette(object): ''' return "%s%s" % (self.baseurl, relative_url) - def start_session(self, desired_capabilities=None): + def start_session(self, desired_capabilities=None, session_id=None): """Create a new Marionette session. This method must be called before performing any other action. @@ -820,7 +823,7 @@ class Marionette(object): capabilities. This is currently ignored. :returns: A dict of the capabilities offered.""" - self.session = self._send_message('newSession', 'value', capabilities=desired_capabilities) + self.session = self._send_message('newSession', 'value', capabilities=desired_capabilities, session_id=session_id) self.b2g = 'b2g' in self.session return self.session @@ -836,6 +839,7 @@ class Marionette(object): def delete_session(self): """Close the current session and disconnect from the server.""" response = self._send_message('deleteSession', 'ok') + self.session_id = None self.session = None self.window = None self.client.close() diff --git a/testing/marionette/client/marionette/tests/unit/test_session.py b/testing/marionette/client/marionette/tests/unit/test_session.py index 16eec76d3a59..6ef600e48821 100644 --- a/testing/marionette/client/marionette/tests/unit/test_session.py +++ b/testing/marionette/client/marionette/tests/unit/test_session.py @@ -29,4 +29,16 @@ class TestSession(marionette_test.MarionetteTestCase): self.assertIn("takesScreenshot", caps) self.assertIn("version", caps) + def test_we_can_get_the_session_id(self): + # Sends newSession + caps = self.marionette.start_session() + self.assertTrue(self.marionette.session_id is not None) + self.assertTrue(isinstance(self.marionette.session_id, unicode)) + + def test_we_can_set_the_session_id(self): + # Sends newSession + caps = self.marionette.start_session(session_id="ILoveCheese") + + self.assertEqual(self.marionette.session_id, "ILoveCheese") + self.assertTrue(isinstance(self.marionette.session_id, unicode)) \ No newline at end of file diff --git a/testing/marionette/marionette-server.js b/testing/marionette/marionette-server.js index 810b7f3c7fdd..3279b7485bd6 100644 --- a/testing/marionette/marionette-server.js +++ b/testing/marionette/marionette-server.js @@ -127,6 +127,7 @@ function MarionetteServerConnection(aPrefix, aTransport, aServer) // passing back "actor ids" with responses. unlike the debugger server, // we don't have multiple actors, so just use a dummy value of "0" here this.actorID = "0"; + this.sessionId = null; this.globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"] .getService(Ci.nsIMessageBroadcaster); @@ -330,7 +331,9 @@ MarionetteServerConnection.prototype = { sendResponse: function MDA_sendResponse(value, command_id) { if (typeof(value) == 'undefined') value = null; - this.sendToClient({from:this.actorID, value: value}, command_id); + this.sendToClient({from:this.actorID, + sessionId: this.sessionId, + value: value}, command_id); }, sayHello: function MDA_sayHello() { @@ -553,6 +556,8 @@ MarionetteServerConnection.prototype = { this.scriptTimeout = 10000; if (aRequest && aRequest.parameters) { + this.sessionId = aRequest.parameters.session_id ? aRequest.parameters.session_id : null; + logger.info("Session Id is set to: " + this.sessionId); this.setSessionCapabilities(aRequest.parameters.capabilities); } @@ -625,6 +630,10 @@ MarionetteServerConnection.prototype = { getSessionCapabilities: function MDA_getSessionCapabilities() { this.command_id = this.getCommandId(); + if (!this.sessionId) { + this.sessionId = this.uuidGen.generateUUID().toString(); + } + // eideticker (bug 965297) and mochitest (bug 965304) // compatibility. They only check for the presence of this // property and should so not be in caps if not on a B2G device. @@ -2277,6 +2286,7 @@ MarionetteServerConnection.prototype = { if (this.mainFrame) { this.mainFrame.focus(); } + this.sessionId = null; this.deleteFile('marionetteChromeScripts'); this.deleteFile('marionetteContentScripts'); }, diff --git a/testing/web-platform/meta/XMLHttpRequest/send-entity-body-basic.htm.ini b/testing/web-platform/meta/XMLHttpRequest/send-entity-body-basic.htm.ini deleted file mode 100644 index 00ca3d02a9d1..000000000000 --- a/testing/web-platform/meta/XMLHttpRequest/send-entity-body-basic.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[send-entity-body-basic.htm] - type: testharness - [XMLHttpRequest: send() - data argument (2,2)] - expected: FAIL - diff --git a/testing/web-platform/meta/resource-timing/test_resource_timing.html.ini b/testing/web-platform/meta/resource-timing/test_resource_timing.html.ini new file mode 100644 index 000000000000..b08fa726928c --- /dev/null +++ b/testing/web-platform/meta/resource-timing/test_resource_timing.html.ini @@ -0,0 +1,3 @@ +[test_resource_timing.html] + type: testharness + disabled: 1079837 diff --git a/toolkit/components/filepicker/content/filepicker.js b/toolkit/components/filepicker/content/filepicker.js index e6552e8aaef9..117f9994a5c0 100644 --- a/toolkit/components/filepicker/content/filepicker.js +++ b/toolkit/components/filepicker/content/filepicker.js @@ -45,13 +45,13 @@ function filepickerLoad() { const title = o.title; filePickerMode = o.mode; if (o.displayDirectory) { - const directory = o.displayDirectory.path; + var directory = o.displayDirectory.path; } const initialText = o.defaultString; - const filterTitles = o.filters.titles; - const filterTypes = o.filters.types; - const numFilters = filterTitles.length; + var filterTitles = o.filters.titles; + var filterTypes = o.filters.types; + var numFilters = filterTitles.length; document.title = title; allowURLs = o.allowURLs; diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 60fa05a7fb42..1d5c09943c83 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -998,7 +998,8 @@ private: { nsPurpleBufferEntry* eEnd = ArrayEnd(mEntries); for (nsPurpleBufferEntry* e = mEntries; e != eEnd; ++e) { - if (!(uintptr_t(e->mObject) & uintptr_t(1))) { + MOZ_ASSERT(e->mObject, "There should be no null mObject when we iterate over the purple buffer"); + if (!(uintptr_t(e->mObject) & uintptr_t(1)) && e->mObject) { aVisitor.Visit(aBuffer, e); } }