diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 71f558c777e1..948a74da466e 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -520,6 +520,9 @@ @RESPATH@/components/remotebrowserutils.manifest @RESPATH@/components/RemoteWebNavigation.js +@RESPATH@/components/ProcessSelector.js +@RESPATH@/components/ProcessSelector.manifest + @RESPATH@/components/SlowScriptDebug.manifest @RESPATH@/components/SlowScriptDebug.js diff --git a/dom/base/ProcessSelector.js b/dom/base/ProcessSelector.js new file mode 100644 index 000000000000..97ea245d1651 --- /dev/null +++ b/dom/base/ProcessSelector.js @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import('resource://gre/modules/Services.jsm'); + +const BASE_PREF = "dom.ipc.processCount" +const PREF_BRANCH = BASE_PREF + "."; + +// Utilities: +function getMaxContentParents(processType) { + let maxContentParents = -1; + try { + maxContentParents = Services.prefs.getIntPref(PREF_BRANCH + processType); + } catch (e) { + // Pref probably didn't exist, get the default number of processes. + try { + maxContentParents = Services.prefs.getIntPref(BASE_PREF); + } catch (e) { + // No prefs? That's odd, use only one process. + maxContentParents = 1; + } + } + + return maxContentParents; +} + +// Fills up aProcesses until max and then selects randomly from the available +// ones. +function RandomSelector() { +} + +RandomSelector.prototype = { + classID: Components.ID("{c616fcfd-9737-41f1-aa74-cee72a38f91b}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]), + + provideProcess(aType, aOpener, aProcesses, aCount) { + let maxContentParents = getMaxContentParents(aType); + if (aCount < maxContentParents) { + return Ci.nsIContentProcessProvider.NEW_PROCESS; + } + + let startIdx = Math.floor(Math.random() * maxContentParents); + let curIdx = startIdx; + + do { + if (aProcesses[curIdx].opener === aOpener) { + return curIdx; + } + + curIdx = (curIdx + 1) % maxContentParents; + } while (curIdx !== startIdx); + + return Ci.nsIContentProcessProvider.NEW_PROCESS; + }, +}; + +var components = [RandomSelector]; +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/dom/base/ProcessSelector.manifest b/dom/base/ProcessSelector.manifest new file mode 100644 index 000000000000..8b46c5ab16aa --- /dev/null +++ b/dom/base/ProcessSelector.manifest @@ -0,0 +1,2 @@ +component {c616fcfd-9737-41f1-aa74-cee72a38f91b} ProcessSelector.js +contract @mozilla.org/ipc/processselector;1 {c616fcfd-9737-41f1-aa74-cee72a38f91b} diff --git a/dom/base/TimeoutManager.cpp b/dom/base/TimeoutManager.cpp index e6f3c8ef91df..78d0a62f2122 100644 --- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -71,6 +71,11 @@ uint32_t TimeoutManager::sNestingLevel = 0; namespace { +// The maximum number of timer callbacks we will try to run in a single event +// loop runnable. +#define DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS 5 +uint32_t gTargetMaxConsecutiveCallbacks; + // The number of queued runnables within the TabGroup ThrottledEventQueue // at which to begin applying back pressure to the window. #define DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE 5000 @@ -182,6 +187,10 @@ TimeoutManager::Initialize() Preferences::AddUintVarCache(&gBackPressureDelayMinimumMS, "dom.timeout.back_pressure_delay_minimum_ms", DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS); + + Preferences::AddUintVarCache(&gTargetMaxConsecutiveCallbacks, + "dom.timeout.max_consecutive_callbacks", + DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS); } uint32_t @@ -444,6 +453,10 @@ TimeoutManager::RunTimeout(Timeout* aTimeout) mTrackingTimeouts, nullptr, nullptr); + + uint32_t numTimersToRun = 0; + bool targetTimerSeen = false; + while (true) { Timeout* timeout = expiredIter.Next(); if (!timeout || timeout->When() > deadline) { @@ -461,19 +474,29 @@ TimeoutManager::RunTimeout(Timeout* aTimeout) last_expired_tracking_timeout = timeout; } - // Run available timers until we see our target timer. After - // that, however, stop coalescing timers so we can yield the - // main thread. Further timers that are ready will get picked - // up by their own nsITimer runnables when they execute. + // Note that we have seen our target timer. This means we can now + // stop processing timers once we hit our threshold below. + if (timeout == aTimeout) { + targetTimerSeen = true; + } + + // Run only a limited number of timers based on the configured + // maximum. Note, we must always run our target timer however. + // Further timers that are ready will get picked up by their own + // nsITimer runnables when they execute. // // For chrome windows, however, we do coalesce all timers and // do not yield the main thread. This is partly because we // trust chrome windows not to misbehave and partly because a // number of browser chrome tests have races that depend on this // coalescing. - if (timeout == aTimeout && !mWindow.IsChromeWindow()) { + if (targetTimerSeen && + numTimersToRun >= gTargetMaxConsecutiveCallbacks && + !mWindow.IsChromeWindow()) { break; } + + numTimersToRun += 1; } expiredIter.UpdateIterator(); diff --git a/dom/base/UseCounters.conf b/dom/base/UseCounters.conf index 6473a6b58c9d..22bfa374602c 100644 --- a/dom/base/UseCounters.conf +++ b/dom/base/UseCounters.conf @@ -62,6 +62,12 @@ attribute OfflineResourceList.onupdateready attribute OfflineResourceList.oncached attribute OfflineResourceList.onobsolete +// Non-standard IndexedDB API +method IDBDatabase.createMutableFile +method IDBDatabase.mozCreateFileHandle +method IDBMutableFile.open +method IDBMutableFile.getFile + // DataTransfer API (gecko-only methods) method DataTransfer.addElement attribute DataTransfer.mozItemCount diff --git a/dom/base/moz.build b/dom/base/moz.build index c029ee3587de..bf7d3de30ac6 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -401,6 +401,8 @@ EXTRA_COMPONENTS += [ 'contentAreaDropListener.manifest', 'messageWakeupService.js', 'messageWakeupService.manifest', + 'ProcessSelector.js', + 'ProcessSelector.manifest', 'SlowScriptDebug.js', 'SlowScriptDebug.manifest', ] diff --git a/dom/interfaces/base/moz.build b/dom/interfaces/base/moz.build index e96fc0158ee0..9d7f7acddcb7 100644 --- a/dom/interfaces/base/moz.build +++ b/dom/interfaces/base/moz.build @@ -11,6 +11,7 @@ XPIDL_SOURCES += [ 'nsIContentPermissionPrompt.idl', 'nsIContentPrefService.idl', 'nsIContentPrefService2.idl', + 'nsIContentProcess.idl', 'nsIContentURIGrouper.idl', 'nsIDOMChromeWindow.idl', 'nsIDOMClientRect.idl', diff --git a/dom/interfaces/base/nsIContentProcess.idl b/dom/interfaces/base/nsIContentProcess.idl new file mode 100644 index 000000000000..2aa21fa5a804 --- /dev/null +++ b/dom/interfaces/base/nsIContentProcess.idl @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIDOMElement; +interface nsIMessageSender; +interface nsIURI; + +[scriptable, builtinclass, uuid(456f58be-29dd-4973-885b-95aece1c9a8a)] +interface nsIContentProcessInfo : nsISupports +{ + /** + * Is this content process alive? + */ + readonly attribute boolean isAlive; + + /** + * The content process's PID. + * Throws if the process is not alive. + */ + readonly attribute int32_t processId; + + /** + * This content process's opener. + */ + readonly attribute nsIContentProcessInfo opener; + + /** + * The process manager for this ContentParent (so a process message manager + * as opposed to a frame message manager. + */ + readonly attribute nsIMessageSender messageManager; +}; + +[scriptable, uuid(83ffb063-5f65-4c45-ae07-3f553e0809bb)] +interface nsIContentProcessProvider : nsISupports +{ + /** + * Return this from provideProcess to create a new process. + */ + const int32_t NEW_PROCESS = -1; + + /** + * Given aAliveProcesses (with an opener aOpener), choose which process of + * aType to use. Return nsIContentProcessProvider.NEW_PROCESS to ask the + * caller to create a new content process. + */ + int32_t provideProcess(in AString aType, in nsIContentProcessInfo aOpener, + [array, size_is(aCount)] in nsIContentProcessInfo aAliveProcesses, + in uint32_t aCount); +}; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index fc293095918b..76ce3b5aed89 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -112,6 +112,7 @@ #include "nsIAlertsService.h" #include "nsIClipboard.h" #include "nsContentPermissionHelper.h" +#include "nsIContentProcess.h" #include "nsICycleCollectorListener.h" #include "nsIDocShellTreeOwner.h" #include "nsIDocument.h" @@ -435,6 +436,90 @@ ContentParentsMemoryReporter::CollectReports( } nsClassHashtable>* ContentParent::sBrowserContentParents; + +namespace { + +class ScriptableCPInfo final : public nsIContentProcessInfo +{ +public: + explicit ScriptableCPInfo(ContentParent* aParent) + : mContentParent(aParent) + { + MOZ_ASSERT(mContentParent); + } + + NS_DECL_ISUPPORTS + NS_DECL_NSICONTENTPROCESSINFO + + void ProcessDied() + { + mContentParent = nullptr; + } + +private: + ~ScriptableCPInfo() + { + MOZ_ASSERT(!mContentParent, "must call ProcessDied"); + } + + ContentParent* mContentParent; +}; + +NS_IMPL_ISUPPORTS(ScriptableCPInfo, nsIContentProcessInfo) + +NS_IMETHODIMP +ScriptableCPInfo::GetIsAlive(bool* aIsAlive) +{ + *aIsAlive = mContentParent != nullptr; + return NS_OK; +} + +NS_IMETHODIMP +ScriptableCPInfo::GetProcessId(int32_t* aPID) +{ + if (!mContentParent) { + *aPID = -1; + return NS_ERROR_NOT_INITIALIZED; + } + + *aPID = mContentParent->Pid(); + if (*aPID == -1) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +ScriptableCPInfo::GetOpener(nsIContentProcessInfo** aInfo) +{ + *aInfo = nullptr; + if (!mContentParent) { + return NS_ERROR_NOT_INITIALIZED; + } + + if (ContentParent* opener = mContentParent->Opener()) { + nsCOMPtr info = opener->ScriptableHelper(); + info.forget(aInfo); + } + return NS_OK; +} + +NS_IMETHODIMP +ScriptableCPInfo::GetMessageManager(nsIMessageSender** aMessenger) +{ + *aMessenger = nullptr; + if (!mContentParent) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr manager = mContentParent->GetMessageManager(); + manager.forget(aMessenger); + return NS_OK; +} + +} // anonymous namespace + nsTArray* ContentParent::sPrivateContent; StaticAutoPtr > ContentParent::sContentParents; #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) @@ -697,38 +782,65 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType, ContentParent* aOpener) { nsTArray& contentParents = GetOrCreatePool(aRemoteType); - uint32_t maxContentParents = GetMaxProcessCount(aRemoteType); - RefPtr p; - if (contentParents.Length() >= maxContentParents) { + if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) { // We never want to re-use Large-Allocation processes. - if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) { + if (contentParents.Length() >= maxContentParents) { return GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), aPriority, aOpener); } + } else { + nsTArray infos(contentParents.Length()); + for (auto* cp : contentParents) { + infos.AppendElement(cp->mScriptableHelper); + } - if ((p = RandomSelect(contentParents, aOpener, maxContentParents))) { + nsCOMPtr cpp = + do_GetService("@mozilla.org/ipc/processselector;1"); + nsIContentProcessInfo* openerInfo = aOpener ? aOpener->mScriptableHelper.get() : nullptr; + int32_t index; + if (cpp && + NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, openerInfo, + infos.Elements(), infos.Length(), + &index))) { + // If the provider returned an existing ContentParent, use that one. + if (0 <= index && static_cast(index) <= maxContentParents) { + RefPtr retval = contentParents[index]; + return retval.forget(); + } + } else { + // If there was a problem with the JS chooser, fall back to a random + // selection. + NS_WARNING("nsIContentProcessProvider failed to return a process"); + RefPtr random; + if (contentParents.Length() >= maxContentParents && + (random = RandomSelect(contentParents, aOpener, maxContentParents))) { + return random.forget(); + } + } + + // Try to take the preallocated process only for the default process type. + RefPtr p; + if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) && + (p = PreallocatedProcessManager::Take())) { + // For pre-allocated process we have not set the opener yet. + p->mOpener = aOpener; + contentParents.AppendElement(p); return p.forget(); } } - // Try to take the preallocated process only for the default process type. - if (aRemoteType.Equals(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)) && - (p = PreallocatedProcessManager::Take())) { - // For pre-allocated process we have not set the opener yet. - p->mOpener = aOpener; - } else { - p = new ContentParent(aOpener, aRemoteType); + // Create a new process from scratch. + RefPtr p = new ContentParent(aOpener, aRemoteType); - if (!p->LaunchSubprocess(aPriority)) { - return nullptr; - } - - p->Init(); + if (!p->LaunchSubprocess(aPriority)) { + return nullptr; } + p->Init(); + contentParents.AppendElement(p); return p.forget(); } @@ -1204,6 +1316,8 @@ ContentParent::Init() RefPtr gmps(GeckoMediaPluginServiceParent::GetSingleton()); gmps->UpdateContentProcessGMPCapabilities(); + + mScriptableHelper = new ScriptableCPInfo(this); } namespace { @@ -1274,6 +1388,11 @@ ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority) void ContentParent::ShutDownProcess(ShutDownMethod aMethod) { + if (mScriptableHelper) { + static_cast(mScriptableHelper.get())->ProcessDied(); + mScriptableHelper = nullptr; + } + // Shutting down by sending a shutdown message works differently than the // other methods. We first call Shutdown() in the child. After the child is // ready, it calls FinishShutdown() on us. Then we close the channel. diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index e5f023b31b08..115937034000 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -45,6 +45,7 @@ #define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation" class nsConsoleService; +class nsIContentProcessInfo; class nsICycleCollectorLogSink; class nsIDumpGCAndCCLogsCallback; class nsITabParent; @@ -371,6 +372,10 @@ public: { return mOpener; } + nsIContentProcessInfo* ScriptableHelper() const + { + return mScriptableHelper; + } bool NeedsPermissionsUpdate() const { @@ -1173,6 +1178,7 @@ private: RefPtr mConsoleService; nsConsoleService* GetConsoleService(); + nsCOMPtr mScriptableHelper; nsTArray> mIdleListeners; diff --git a/dom/webidl/IDBDatabase.webidl b/dom/webidl/IDBDatabase.webidl index 36fe465f30d7..19ecaa41b3cf 100644 --- a/dom/webidl/IDBDatabase.webidl +++ b/dom/webidl/IDBDatabase.webidl @@ -39,10 +39,10 @@ partial interface IDBDatabase { [Func="mozilla::dom::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] readonly attribute StorageType storage; - [Exposed=Window, Throws] + [Exposed=Window, Throws, UseCounter] IDBRequest createMutableFile (DOMString name, optional DOMString type); // this is deprecated due to renaming in the spec - [Exposed=Window, Throws] + [Exposed=Window, Throws, UseCounter] IDBRequest mozCreateFileHandle (DOMString name, optional DOMString type); // now createMutableFile }; diff --git a/dom/webidl/IDBMutableFile.webidl b/dom/webidl/IDBMutableFile.webidl index 945a79d77692..4883cb22037e 100644 --- a/dom/webidl/IDBMutableFile.webidl +++ b/dom/webidl/IDBMutableFile.webidl @@ -10,10 +10,10 @@ interface IDBMutableFile : EventTarget { readonly attribute IDBDatabase database; - [Throws] + [Throws, UseCounter] IDBFileHandle open(optional FileMode mode = "readonly"); - [Throws] + [Throws, UseCounter] DOMRequest getFile(); attribute EventHandler onabort; diff --git a/intl/hyphenation/hyphen/NEWS b/intl/hyphenation/hyphen/NEWS new file mode 100755 index 000000000000..efaa78b24994 --- /dev/null +++ b/intl/hyphenation/hyphen/NEWS @@ -0,0 +1,106 @@ +2014-09-18 Hyphen 2.8.8: + - remove last coverity warning, 0 remaining + +2014-06-27 Hyphen 2.8.7: + - various clang scan-build warning fixes + +2012-09-13 Hyphen 2.8.6: + - righthyphenmin fix for 3-byte or more UTF-8 + multibyte characters by Steven Dickson + - fix for fdo#43931 (removing hard hyphen hyphenation for LibreOffice) + +2012-07-12 Hyphen 2.8.5: + - fix short alloc + +2012-06-29 Hyphen 2.8.4: + - coverity warnings + +2011-10-10 Hyphen 2.8.3: + - fix NOHYPHEN + - fix unbalanced hyphenation of LibreOffice/OOo + - set default COMPOUNDHYPHENMIN=3 at hyphens and apostrophes + - fix VERBOSE in hyphen.c + - new ./example option: -n to print hyphenation vector + +2011-10-07 Hyphen 2.8.2: + - fix for explicite COMPOUNDHYPHENMIN values + +2011-10-06 Hyphen 2.8.1: + - force minimal lefthyphenmin and righthyphenmin values of the dictionary + (eg. righthyphenmin=3 of English dictionaries in LibreOffice/OOo, + also the original TeX hyphenation patterns are correct only with this + righthyphenmin value). + +2011-10-04 Hyphen 2.8: + - Ignore leading and ending numbers (eg. manual/field based indexes + in LibreOffice/OOo) + + - Fix LibreOffice/OpenOffice.org hyphenation errors at apostrophes and + hyphens, n-dashes with default NOHYPHEN separators. + Eg. *o'c=lock -> o'clock. + +2010-12-01 Hyphen 2.7.1 bug fix release + +2010-11-27 Hyphen 2.7 release: + - The new hyphenation problem of OpenOffice.org 3.2, related to its + modified word breaking of words with hyphen characters, can be fixed + with the new NOHYPHEN feature. Also it's possible to solve the similar old + problem with apostrophes. More information: README.compound. + + - improved English dictionaries + +2010-08-10 Hyphen 2.6 release: + - maintainance release, fix all warnings, tidy up + make check with VALGRIND=memcheck, etc. + +2010-02-23 Hyphen 2.5 release: + - add Unicode ligature support for correct hyphenmin calculation + (ff, fi, fl, St, st are 1-character, ffi and ffl are 2-character length for + hyphenation) + - fix lefthyphenmin calculation for UTF-8 encoded input + + - en_US hyphenation dictionary: + - add OpenOffice.org patch to fix apostrophe handling + - add correct hyphenation for words with Unicode f-ligatures + (NOTE: hyphenation within ligatures is not supported yet + because of an implementation problem of OpenOffice.org, + see OOo issue 71608.) + + - small patches from OpenOffice.org + +2008-05-01 Hyphen 2.4 release: + - compound word hyphenation support by recursive pattern matching + based on two hyphenation pattern sets, see README.compound. + Especially useful for languages with arbitrary number of compounds (Danish, + Dutch, Finnish, German, Hungarian, Icelandic, Norwegian, Swedish etc.). + + - new dictionary parameters (minimal character numbers for hyph. distances): + LEFTHYPHENMIN: minimal hyphenation distance from the left end of the word + RIGHTHYPHENMIN: minimal hyphenation distance from the right end of the word + COMPOUNDLEFTHYPHENMIN: min. hyph. dist. from the left compound word boundary + COMPOUNDRIGHTHYPHENMIN: min. hyph. dist. from the right comp. word boundary + + - new API function: hnj_hyphen_hyphenate3() (like hyphenate2(), but + with hyphenmin options) + +en_US hyphenation patterns: + + - extended hyph_en_US.dic with TugBoat hyphenation log (fix thousand + incompletely or badly hyphenated words, for example acad-e-my, acro-nym, + acryl-amide, adren-a-line, aero-space, am-phet-a-mine, anom-aly etc.) + + - fixed hyph_en_US.dic: set the right default hyphenation distance of + the original TeX hyphenation patterns: + LEFTHYPHENMIN 2 + RIGHTHYPHENMIN 3 (not 2!) + It is not only a typographical issue. It seems, TeX hyphenation + patterns are right only with these settings, for example, + the bad "anoma-ly" is restricted in TeX only by the default + \righthyphenmin=3 (but not restricted in OpenOffice.org, until now). + + - documentation (README_hyph_en_US.dic) + + - fixes for automake configuration, compiling and checking, see ChangeLog + +2008-02-19: Hyphen 2.3.1 release: + - fix obsolete API function hnj_hyphen_hyphenate() diff --git a/js/src/frontend/NameAnalysisTypes.h b/js/src/frontend/NameAnalysisTypes.h index b94ca6cc08e3..2d327c8272db 100644 --- a/js/src/frontend/NameAnalysisTypes.h +++ b/js/src/frontend/NameAnalysisTypes.h @@ -78,6 +78,7 @@ enum class DeclarationKind : uint8_t Const, Import, BodyLevelFunction, + ModuleBodyLevelFunction, LexicalFunction, VarForAnnexBLexicalFunction, SimpleCatchParameter, @@ -95,6 +96,7 @@ DeclarationKindToBindingKind(DeclarationKind kind) case DeclarationKind::Var: case DeclarationKind::BodyLevelFunction: + case DeclarationKind::ModuleBodyLevelFunction: case DeclarationKind::VarForAnnexBLexicalFunction: case DeclarationKind::ForOfVar: return BindingKind::Var; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0f0184060da7..0cc8c956be60 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -117,6 +117,7 @@ DeclarationKindString(DeclarationKind kind) case DeclarationKind::Import: return "import"; case DeclarationKind::BodyLevelFunction: + case DeclarationKind::ModuleBodyLevelFunction: case DeclarationKind::LexicalFunction: return "function"; case DeclarationKind::VarForAnnexBLexicalFunction: @@ -1379,6 +1380,24 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind break; } + case DeclarationKind::ModuleBodyLevelFunction: { + MOZ_ASSERT(pc->atModuleLevel()); + + AddDeclaredNamePtr p = pc->varScope().lookupDeclaredNameForAdd(name); + if (p) { + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); + return false; + } + + if (!pc->varScope().addDeclaredName(pc, p, name, kind, pos.begin)) + return false; + + // Body-level functions in modules are always closed over. + pc->varScope().lookupDeclaredName(name)->value()->setClosedOver(); + + break; + } + case DeclarationKind::FormalParameter: { // It is an early error if any non-positional formal parameter name // (e.g., destructuring formal parameter) is duplicated. @@ -3718,12 +3737,11 @@ Parser::functionStmt(YieldHandling yieldHandling, DefaultHandling if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos())) return null(); } else { - if (!noteDeclaredName(name, DeclarationKind::BodyLevelFunction, pos())) + DeclarationKind kind = pc->atModuleLevel() + ? DeclarationKind::ModuleBodyLevelFunction + : DeclarationKind::BodyLevelFunction; + if (!noteDeclaredName(name, kind, pos())) return null(); - - // Body-level functions in modules are always closed over. - if (pc->atModuleLevel()) - pc->varScope().lookupDeclaredName(name)->value()->setClosedOver(); } Node pn = handler.newFunctionStatement(); diff --git a/js/src/jit-test/modules/export-default-async-asi.js b/js/src/jit-test/modules/export-default-async-asi.js index db3d1bf19b44..a69a7aa3dc0c 100644 --- a/js/src/jit-test/modules/export-default-async-asi.js +++ b/js/src/jit-test/modules/export-default-async-asi.js @@ -1,4 +1,2 @@ -function async() { return 42; } // overwritten by subsequent declaration - export default async // ASI occurs here due to the [no LineTerminator here] restriction on default-exporting an async function function async() { return 17; } diff --git a/js/src/jit-test/tests/modules/function-redeclaration.js b/js/src/jit-test/tests/modules/function-redeclaration.js new file mode 100644 index 000000000000..b84704641d2f --- /dev/null +++ b/js/src/jit-test/tests/modules/function-redeclaration.js @@ -0,0 +1,94 @@ +load(libdir + "asserts.js"); + +var functionDeclarations = [ + "function f(){}", + "function* f(){}", + "async function f(){}", +]; + +var varDeclarations = [ + "var f", + "{ var f; }", + "for (var f in null);", + "for (var f of null);", + "for (var f; ;);", +]; + +var lexicalDeclarations = [ + "let f;", + "const f = 0;", + "class f {};", +]; + +var imports = [ + "import f from '';", + "import f, {} from '';", + "import d, {f} from '';", + "import d, {f as f} from '';", + "import d, {foo as f} from '';", + "import f, * as d from '';", + "import d, * as f from '';", + "import {f} from '';", + "import {f as f} from '';", + "import {foo as f} from '';", + "import* as f from '';", +]; + +var exports = [ + "export var f;", + ...functionDeclarations.map(fn => `export ${fn};`), + ...lexicalDeclarations.map(ld => `export ${ld};`), + ...functionDeclarations.map(fn => `export default ${fn};`), + "export default class f {};", +]; + +var redeclarations = [ + ...functionDeclarations, + ...varDeclarations, + ...lexicalDeclarations, + ...imports, + ...exports, +]; + +var noredeclarations = [ + ...functionDeclarations.map(fn => `{ ${fn} }`), + ...lexicalDeclarations.map(ld => `{ ${ld} }`), + ...["let", "const"].map(ld => `for (${ld} f in null);`), + ...["let", "const"].map(ld => `for (${ld} f of null);`), + ...["let", "const"].map(ld => `for (${ld} f = 0; ;);`), + "export {f};", + "export {f as f};", + "export {foo as f}; var foo;", + "export {f} from '';", + "export {f as f} from '';", + "export {foo as f} from '';", +]; + +for (var decl of functionDeclarations) { + for (var redecl of redeclarations) { + assertThrowsInstanceOf(() => { + parseModule(` + ${decl} + ${redecl} + `); + }, SyntaxError); + + assertThrowsInstanceOf(() => { + parseModule(` + ${redecl} + ${decl} + `); + }, SyntaxError); + } + + for (var redecl of noredeclarations) { + parseModule(` + ${decl} + ${redecl} + `); + parseModule(` + ${redecl} + ${decl} + `); + } +} diff --git a/js/src/jit-test/tests/wasm/import-export-sigs.js b/js/src/jit-test/tests/wasm/import-export-sigs.js new file mode 100644 index 000000000000..e5985181cfb2 --- /dev/null +++ b/js/src/jit-test/tests/wasm/import-export-sigs.js @@ -0,0 +1,88 @@ +// Tests that function imports and function exports descriptors have +// signatures, in the test mode only, for fuzzers. + +var module = new WebAssembly.Module(wasmTextToBinary(`(module + (import $vv "env" "v_v") + (export "v_v" $vv) + + (import $vi "env" "v_i" (param i32)) + (export "v_i" $vi) + + (import $vI "env" "v_I" (param i64)) + (export "v_I" $vI) + + (import $vf "env" "v_f" (param f32)) + (export "v_f" $vf) + + (import $mem "env" "memory" (memory 0)) + (export "mem" (memory $mem)) + + (import $vd "env" "v_d" (param f64)) + (export "v_d" $vd) + + (import $vfd "env" "v_fd" (param f32) (param f64)) + (export "v_fd" $vfd) + + (import $vIfifd "env" "v_Ififd" (param i64) (param f32) (param i32) (param f32) (param f64)) + (export "v_Ififd" $vIfifd) + + (import $iv "env" "i_v" (result i32)) + (export "i_v" $iv) + + (import $Ii "env" "I_i" (result i64) (param i32)) + (export "I_i" $Ii) + + (import $table "env" "table" (table 0 anyfunc)) + (export "table" (table $table)) + + (import $fd "env" "f_d" (result f32) (param f64)) + (export "f_d" $fd) + + (import $dffd "env" "d_ffd" (result f64) (param f32) (param f32) (param f64)) + (export "d_ffd" $dffd) +)`)); + +for (let desc of WebAssembly.Module.imports(module)) { + assertEq(typeof desc.signature, 'undefined'); +} +for (let desc of WebAssembly.Module.exports(module)) { + assertEq(typeof desc.signature, 'undefined'); +} + +setJitCompilerOption('wasm.test-mode', 1); + +function shortToType(s) { + switch (s) { + case 'v': return ""; + case 'i': return "i32"; + case 'I': return "i64"; + case 'f': return "f32"; + case 'd': return "f64"; + } + throw new Error("unexpected shorthand type"); +} + +function expectedSignature(name) { + let [ret, args] = name.split('_'); + + args = args.split('').map(shortToType).join(', '); + ret = shortToType(ret); + + return `(${args}) -> (${ret})`; +} + +for (let desc of WebAssembly.Module.imports(module)) { + if (desc.kind !== 'function') { + assertEq(typeof desc.signature, 'undefined'); + } else { + assertEq(desc.signature, expectedSignature(desc.name)) + } +} + +for (let desc of WebAssembly.Module.exports(module)) { + if (desc.kind !== 'function') { + assertEq(typeof desc.signature, 'undefined'); + } else { + assertEq(desc.signature, expectedSignature(desc.name)) + } +} diff --git a/js/src/tests/test262-intl-extras.js b/js/src/tests/test262-intl-extras.js index d8c92b6d29ab..75a9891d0cf5 100644 --- a/js/src/tests/test262-intl-extras.js +++ b/js/src/tests/test262-intl-extras.js @@ -3,5 +3,12 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Call the shell helper to add experimental features to the Intl object. -if (typeof addIntlExtras === "function") - addIntlExtras(Intl); +if (typeof addIntlExtras === "function") { + let intlExtras = {}; + addIntlExtras(intlExtras); + + Object.defineProperty(Intl, "PluralRules", { + value: intlExtras.PluralRules, + writable: true, enumerable: false, configurable: true + }); +} diff --git a/js/src/tests/test262/intl402/PluralRules/shell.js b/js/src/tests/test262/intl402/PluralRules/shell.js index fcb524be040b..a0c393700249 100644 --- a/js/src/tests/test262/intl402/PluralRules/shell.js +++ b/js/src/tests/test262/intl402/PluralRules/shell.js @@ -4,5 +4,12 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Call the shell helper to add experimental features to the Intl object. -if (typeof addIntlExtras === "function") - addIntlExtras(Intl); +if (typeof addIntlExtras === "function") { + let intlExtras = {}; + addIntlExtras(intlExtras); + + Object.defineProperty(Intl, "PluralRules", { + value: intlExtras.PluralRules, + writable: true, enumerable: false, configurable: true + }); +} diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index bc7820c3cf14..39cee4944572 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1500,8 +1500,19 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleId id, HandleVa } } - if (obj->isNative() && !JSID_IS_INT(id) && !JSObject::setHadElementsAccess(cx, obj)) - return false; + // Set the HadElementsAccess flag on the object if needed. This flag is + // used to do more eager dictionary-mode conversion for objects that are + // used as hashmaps. Set this flag only for objects with many properties, + // to avoid unnecessary Shape changes. + if (obj->isNative() && + JSID_IS_ATOM(id) && + !obj->as().inDictionaryMode() && + !obj->hadElementsAccess() && + obj->as().slotSpan() > PropertyTree::MAX_HEIGHT_WITH_ELEMENTS_ACCESS / 3) + { + if (!JSObject::setHadElementsAccess(cx, obj)) + return false; + } ObjectOpResult result; return SetProperty(cx, obj, id, value, receiver, result) && diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 8dfe419501b8..1bbf5e336d3c 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -28,6 +28,7 @@ #include "jit/JitOptions.h" #include "vm/Interpreter.h" #include "vm/String.h" +#include "vm/StringBuffer.h" #include "wasm/WasmCompile.h" #include "wasm/WasmInstance.h" #include "wasm/WasmModule.h" @@ -554,8 +555,9 @@ struct KindNames RootedPropertyName kind; RootedPropertyName table; RootedPropertyName memory; + RootedPropertyName signature; - explicit KindNames(JSContext* cx) : kind(cx), table(cx), memory(cx) {} + explicit KindNames(JSContext* cx) : kind(cx), table(cx), memory(cx), signature(cx) {} }; static bool @@ -576,6 +578,11 @@ InitKindNames(JSContext* cx, KindNames* names) return false; names->memory = memory->asPropertyName(); + JSAtom* signature = Atomize(cx, "signature", strlen("signature")); + if (!signature) + return false; + names->signature = signature->asPropertyName(); + return true; } @@ -596,6 +603,40 @@ KindToString(JSContext* cx, const KindNames& names, DefinitionKind kind) MOZ_CRASH("invalid kind"); } +static JSString* +SigToString(JSContext* cx, const Sig& sig) +{ + StringBuffer buf(cx); + if (!buf.append('(')) + return nullptr; + + bool first = true; + for (ValType arg : sig.args()) { + if (!first && !buf.append(", ", strlen(", "))) + return nullptr; + + const char* argStr = ToCString(arg); + if (!buf.append(argStr, strlen(argStr))) + return nullptr; + + first = false; + } + + if (!buf.append(") -> (", strlen(") -> ("))) + return nullptr; + + if (sig.ret() != ExprType::Void) { + const char* retStr = ToCString(sig.ret()); + if (!buf.append(retStr, strlen(retStr))) + return nullptr; + } + + if (!buf.append(')')) + return nullptr; + + return buf.finishString(); +} + static JSString* UTF8CharsToString(JSContext* cx, const char* chars) { @@ -619,6 +660,7 @@ WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) if (!elems.reserve(module->imports().length())) return false; + size_t numFuncImport = 0; for (const Import& import : module->imports()) { Rooted props(cx, IdValueVector(cx)); if (!props.reserve(3)) @@ -639,6 +681,14 @@ WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) return false; props.infallibleAppend(IdValuePair(NameToId(names.kind), StringValue(kindStr))); + if (JitOptions.wasmTestMode && import.kind == DefinitionKind::Function) { + JSString* sigStr = SigToString(cx, module->metadata().funcImports[numFuncImport++].sig()); + if (!sigStr) + return false; + if (!props.append(IdValuePair(NameToId(names.signature), StringValue(sigStr)))) + return false; + } + JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(), props.length(), GenericObject); if (!obj) return false; @@ -671,6 +721,7 @@ WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) if (!elems.reserve(module->exports().length())) return false; + size_t numFuncExport = 0; for (const Export& exp : module->exports()) { Rooted props(cx, IdValueVector(cx)); if (!props.reserve(2)) @@ -686,6 +737,14 @@ WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) return false; props.infallibleAppend(IdValuePair(NameToId(names.kind), StringValue(kindStr))); + if (JitOptions.wasmTestMode && exp.kind() == DefinitionKind::Function) { + JSString* sigStr = SigToString(cx, module->metadata().funcExports[numFuncExport++].sig()); + if (!sigStr) + return false; + if (!props.append(IdValuePair(NameToId(names.signature), StringValue(sigStr)))) + return false; + } + JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(), props.length(), GenericObject); if (!obj) return false; diff --git a/layout/base/crashtests/1338772-1.html b/layout/base/crashtests/1338772-1.html index c490b50c31e0..70bf09dcaa22 100644 --- a/layout/base/crashtests/1338772-1.html +++ b/layout/base/crashtests/1338772-1.html @@ -21,8 +21,20 @@ function loadHandler() { inner.height = "5px"; inner.offsetTop; } + +// This function is a hack to avoid sporadic test-failures with... +// "...timed out waiting for reftest-wait to be removed". +// Occasionally, it seems this test loses a race condition of some sort, and +// its resize handler isn't invoked. When that happens (and specifically, when +// the test runs for longer than 500ms), we clear reftest-wait and call the +// run a "pass" (which is kind of valid, because we didn't crash!) and move on. +function setupFailsafe() { + setTimeout(() => { + document.documentElement.removeAttribute("class"); + }, 500); +} - +