mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
f77a18b1b7
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1287426 - Clobber required because of Linux Chromium sandbox file moves.
|
||||
Bug 1288460 requires another clobber due to bug 1298779.
|
||||
|
@ -258,6 +258,24 @@ EventTree::FindOrInsert(Accessible* aContainer)
|
||||
|
||||
// We got a match.
|
||||
if (parent->Parent() == node->mContainer) {
|
||||
// Reject the node if it's contained by a show/hide event target
|
||||
uint32_t evCount = node->mDependentEvents.Length();
|
||||
for (uint32_t idx = 0; idx < evCount; idx++) {
|
||||
AccMutationEvent* ev = node->mDependentEvents[idx];
|
||||
if (ev->GetAccessible() == parent) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabledAll(logging::eEventTree |
|
||||
logging::eVerbose)) {
|
||||
logging::MsgBegin("EVENTS_TREE",
|
||||
"Rejecting node since contained by show/hide target");
|
||||
logging::AccessibleInfo("Container", aContainer);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return node->FindOrInsert(aContainer);
|
||||
}
|
||||
|
||||
|
@ -446,6 +446,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert accessibles with a child node moved by aria-owns
|
||||
* Markup:
|
||||
* <div id="t6_fc">
|
||||
* <div id="t6_owns"></div>
|
||||
* </div>
|
||||
* <div id="t6_sc" aria-owns="t6_owns"></div>
|
||||
*/
|
||||
function test6()
|
||||
{
|
||||
this.parent = getNode("t6");
|
||||
this.fc = document.createElement("div");
|
||||
this.fc.setAttribute("id", "t6_fc");
|
||||
this.owns = document.createElement("div");
|
||||
this.owns.setAttribute("id", "t6_owns");
|
||||
this.sc = document.createElement("div");
|
||||
this.sc.setAttribute("id", "t6_sc");
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, this.fc),
|
||||
new invokerChecker(EVENT_SHOW, this.sc),
|
||||
new invokerChecker(EVENT_REORDER, this.parent),
|
||||
new unexpectedInvokerChecker(EVENT_REORDER, this.fc),
|
||||
new unexpectedInvokerChecker(EVENT_REORDER, this.sc),
|
||||
new unexpectedInvokerChecker(EVENT_HIDE, this.owns),
|
||||
new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
|
||||
];
|
||||
|
||||
this.invoke = function test6_invoke()
|
||||
{
|
||||
getNode("t6").appendChild(this.fc);
|
||||
getNode("t6_fc").appendChild(this.owns);
|
||||
getNode("t6").appendChild(this.sc);
|
||||
getNode("t6_sc").setAttribute("aria-owns", "t6_owns");
|
||||
};
|
||||
|
||||
this.getID = function test6_getID() {
|
||||
return "Insert accessibles with a child node moved by aria-owns";
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests.
|
||||
|
||||
@ -477,6 +518,7 @@
|
||||
gQueue.push(new test3());
|
||||
gQueue.push(new test4());
|
||||
gQueue.push(new test5());
|
||||
gQueue.push(new test6());
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
@ -567,5 +609,8 @@
|
||||
<div role="option" id="t5_o">opt</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="t6">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -425,12 +425,6 @@ Section "-Application" APP_IDX
|
||||
${Else}
|
||||
WriteRegDWORD HKCU "$0" "IconsVisible" 0
|
||||
${EndIf}
|
||||
${If} ${AtLeastWin8}
|
||||
${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
|
||||
$AppUserModelID \
|
||||
"FirefoxURL" \
|
||||
"FirefoxHTML"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
!ifdef MOZ_MAINTENANCE_SERVICE
|
||||
|
@ -2,29 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
; The registration ID of the COM server which is used for choosing wether
|
||||
; to launch the Win8 metro browser or desktop browser.
|
||||
!define DELEGATE_EXECUTE_HANDLER_ID {5100FEC1-212B-4BF5-9BF8-3E650FD794A3}
|
||||
;
|
||||
; Defines for adjust token privs and for enumerating keys
|
||||
!ifndef TOKEN_QUERY
|
||||
!define TOKEN_QUERY 0x0008
|
||||
!endif
|
||||
!ifndef TOKEN_ADJUST_PRIVILEGES
|
||||
!define TOKEN_ADJUST_PRIVILEGES 0x0020
|
||||
!endif
|
||||
!ifndef SE_RESTORE_NAME
|
||||
!define SE_RESTORE_NAME SeRestorePrivilege
|
||||
!endif
|
||||
!ifndef SE_PRIVILEGE_ENABLED
|
||||
!define SE_PRIVILEGE_ENABLED 0x00000002
|
||||
!endif
|
||||
!ifndef HKEY_USERS
|
||||
!define HKEY_USERS 0x80000003
|
||||
!endif
|
||||
|
||||
!macro PostUpdate
|
||||
|
||||
; PostUpdate is called from both session 0 and from the user session
|
||||
; for service updates, make sure that we only register with the user session
|
||||
; Otherwise ApplicationID::Set can fail intermittently with a file in use error.
|
||||
@ -191,14 +169,6 @@
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
; Register the DEH
|
||||
${If} ${AtLeastWin8}
|
||||
${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
|
||||
$AppUserModelID \
|
||||
"FirefoxURL" \
|
||||
"FirefoxHTML"
|
||||
${EndIf}
|
||||
!macroend
|
||||
!define PostUpdate "!insertmacro PostUpdate"
|
||||
|
||||
@ -831,168 +801,6 @@ ${EndIf}
|
||||
!macroend
|
||||
!define RemoveDeprecatedKeys "!insertmacro RemoveDeprecatedKeys"
|
||||
|
||||
; Resets Win8+ specific toast keys Windows sets. We call this on a
|
||||
; fresh install and on uninstall.
|
||||
!macro ResetWin8PromptKeys KEY PREFIX
|
||||
${If} ${AtLeastWin8}
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.htm"
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.html"
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.xht"
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.xhtml"
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.shtml"
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_ftp"
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_http"
|
||||
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_https"
|
||||
${EndIf}
|
||||
!macroend
|
||||
!define ResetWin8PromptKeys "!insertmacro ResetWin8PromptKeys"
|
||||
|
||||
; Adds SE_RESTORE_NAME privs
|
||||
!macro AcquireSERestoreName
|
||||
StrCpy $R1 0
|
||||
|
||||
System::Call "kernel32::GetCurrentProcess() i .R0"
|
||||
System::Call "advapi32::OpenProcessToken(i R0, i ${TOKEN_QUERY}|${TOKEN_ADJUST_PRIVILEGES}, \
|
||||
*i R1R1) i .R0"
|
||||
${If} $R0 != 0
|
||||
System::Call "advapi32::LookupPrivilegeValue(t n, t '${SE_RESTORE_NAME}', *l .R2) i .R0"
|
||||
${If} $R0 != 0
|
||||
System::Call "*(i 1, l R2, i ${SE_PRIVILEGE_ENABLED}) i .R0"
|
||||
System::Call "advapi32::AdjustTokenPrivileges(i R1, i 0, i R0, i 0, i 0, i 0)"
|
||||
System::Free $R0
|
||||
${EndIf}
|
||||
System::Call "kernel32::CloseHandle(i R1)"
|
||||
${EndIf}
|
||||
!macroend
|
||||
!define AcquireSERestoreName "!insertmacro AcquireSERestoreName"
|
||||
!define un.AcquireSERestoreName "!insertmacro AcquireSERestoreName"
|
||||
|
||||
; Mounts all user ntuser.dat files into the registry as a subkey of HKU
|
||||
!macro MountRegistryIntoHKU
|
||||
; $0 is used as an index for HKEY_USERS enumeration
|
||||
StrCpy $0 0
|
||||
${Do}
|
||||
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
|
||||
${If} $1 == ""
|
||||
${Break}
|
||||
${EndIf}
|
||||
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
|
||||
System::Call "advapi32::RegLoadKey(i ${HKEY_USERS}, t 'User-$0', t '$2\ntuser.dat')"
|
||||
System::Call "advapi32::RegLoadKey(i ${HKEY_USERS}, t 'User-$0_Classes', t '$2\AppData\Local\Microsoft\Windows\UsrClass.dat')"
|
||||
IntOp $0 $0 + 1
|
||||
${Loop}
|
||||
!macroend
|
||||
!define MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
|
||||
!define un.MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
|
||||
|
||||
; Unmounts all user ntuser.dat files into the registry as a subkey of HKU
|
||||
!macro UnmountRegistryIntoHKU
|
||||
; $0 is used as an index for HKEY_USERS enumeration
|
||||
StrCpy $0 0
|
||||
${Do}
|
||||
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
|
||||
${If} $1 == ""
|
||||
${Break}
|
||||
${EndIf}
|
||||
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
|
||||
System::Call "advapi32::RegUnLoadKey(i ${HKEY_USERS}, t 'User-$0')"
|
||||
System::Call "advapi32::RegUnLoadKey(i ${HKEY_USERS}, t 'User-$0_Classes')"
|
||||
IntOp $0 $0 + 1
|
||||
${Loop}
|
||||
!macroend
|
||||
!define UnmountRegistryIntoHKU "!insertmacro UnmountRegistryIntoHKU"
|
||||
!define un.UnmountRegistryIntoHKU "!insertmacro UnmountRegistryIntoHKU"
|
||||
|
||||
; Unconditionally removes the delegate execute handler registration used to
|
||||
; launch the metro browser and misc. metro related registry values.
|
||||
!macro RemoveDEHRegistration DELEGATE_EXECUTE_HANDLER_ID \
|
||||
APP_USER_MODEL_ID \
|
||||
PROTOCOL_ACTIVATION_ID \
|
||||
FILE_ACTIVATION_ID
|
||||
${AcquireSERestoreName}
|
||||
${MountRegistryIntoHKU}
|
||||
|
||||
; $0 is used as an index for HKEY_USERS enumeration
|
||||
StrCpy $0 0
|
||||
|
||||
${Do}
|
||||
EnumRegKey $1 HKU "" $0
|
||||
${If} $1 == ""
|
||||
${Break}
|
||||
${EndIf}
|
||||
|
||||
ClearErrors
|
||||
${WordFind} "$1" "_Classes" "E#" $3
|
||||
${Unless} ${Errors}
|
||||
; remove the app user model id root registration. We don't need this
|
||||
; here anymore, we just use it for tray registrationdown in widget,
|
||||
; which we read out of the mozilla keys.
|
||||
${If} "${APP_USER_MODEL_ID}" != ""
|
||||
; The removal of this key intermittently fails, so do the best we can in cleanup
|
||||
DeleteRegValue HKU "$1\${APP_USER_MODEL_ID}\.exe\shell\open\command" "DelegateExecute"
|
||||
DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe\shell\open"
|
||||
DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe\shell"
|
||||
DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe"
|
||||
DeleteRegKey HKU "$1\\${APP_USER_MODEL_ID}"
|
||||
${EndIf}
|
||||
;
|
||||
; Remove delegate execute handler clsid registration
|
||||
DeleteRegKey HKU "$1\CLSID\${DELEGATE_EXECUTE_HANDLER_ID}"
|
||||
|
||||
; Remove protocol and file delegate execute handler id assoc
|
||||
DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}" "AppUserModelID"
|
||||
DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}" "AppUserModelID"
|
||||
|
||||
; Remove delegate execute application registry keys
|
||||
DeleteRegKey HKU "$1\${PROTOCOL_ACTIVATION_ID}\Application"
|
||||
DeleteRegKey HKU "$1\${FILE_ACTIVATION_ID}\Application"
|
||||
|
||||
; Remove misc. shell open info
|
||||
DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}\shell\open" "CommandId"
|
||||
DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}\shell\open" "CommandId"
|
||||
DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
|
||||
DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
|
||||
|
||||
; remove metro browser splash image data
|
||||
DeleteRegKey HKU "$1\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData\DefaultBrowser_NOPUBLISHERID\SplashScreen\DefaultBrowser_NOPUBLISHERID!${APP_USER_MODEL_ID}"
|
||||
${Else}
|
||||
; misc. Metro keys
|
||||
DeleteRegKey HKU "$1\Software\Mozilla\Firefox\Metro"
|
||||
DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "CEHDump"
|
||||
DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "MetroD3DAvailable"
|
||||
DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "MetroLastAHE"
|
||||
${ResetWin8PromptKeys} "HKU" "$1\"
|
||||
${EndIf}
|
||||
IntOp $0 $0 + 1
|
||||
${Loop}
|
||||
${UnmountRegistryIntoHKU}
|
||||
|
||||
; The removal of this key intermittently fails, so do the best we can in cleanup
|
||||
${If} "${APP_USER_MODEL_ID}" != ""
|
||||
DeleteRegValue HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell\open\command" "DelegateExecute"
|
||||
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell\open"
|
||||
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell"
|
||||
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe"
|
||||
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}"
|
||||
${EndIf}
|
||||
|
||||
; Remove HKLM entries
|
||||
DeleteRegKey HKLM "Software\Classes\CLSID\${DELEGATE_EXECUTE_HANDLER_ID}"
|
||||
DeleteRegKey HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\Application"
|
||||
DeleteRegKey HKLM "Software\Classes\${FILE_ACTIVATION_ID}\Application"
|
||||
DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
|
||||
DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
|
||||
DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}" "AppUserModelID"
|
||||
DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}" "AppUserModelID"
|
||||
DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\shell\open" "CommandId"
|
||||
DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}\shell\open" "CommandId"
|
||||
|
||||
ClearErrors
|
||||
!macroend
|
||||
|
||||
!define RemoveDEHRegistration "!insertmacro RemoveDEHRegistration"
|
||||
!define un.RemoveDEHRegistration "!insertmacro RemoveDEHRegistration"
|
||||
|
||||
; Removes various directories and files for reasons noted below.
|
||||
!macro RemoveDeprecatedFiles
|
||||
; Some users are ending up with unpacked chrome instead of omni.ja. This
|
||||
|
@ -278,13 +278,6 @@ Section "Uninstall"
|
||||
${un.SetAppLSPCategories}
|
||||
${EndIf}
|
||||
|
||||
${If} ${AtLeastWin8}
|
||||
${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
|
||||
$AppUserModelID \
|
||||
"FirefoxURL" \
|
||||
"FirefoxHTML"
|
||||
${EndIf}
|
||||
|
||||
${un.RegCleanAppHandler} "FirefoxURL"
|
||||
${un.RegCleanAppHandler} "FirefoxHTML"
|
||||
${un.RegCleanProtocolHandler} "ftp"
|
||||
|
@ -33,6 +33,9 @@
|
||||
--toolbarbutton-checkedhover-backgroundcolor: rgba(200,200,200,.5);
|
||||
|
||||
--panel-separator-color: ThreeDShadow;
|
||||
--arrowpanel-dimmed: hsla(210,4%,10%,.07);
|
||||
--arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
|
||||
--arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
|
||||
|
||||
--urlbar-separator-color: ThreeDShadow;
|
||||
}
|
||||
@ -995,7 +998,7 @@ toolbaritem[cui-areatype="menu-panel"] > :-moz-any(@nestedButtons@) > .toolbarbu
|
||||
|
||||
#urlbar-search-footer {
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#urlbar-search-settings {
|
||||
@ -1009,11 +1012,11 @@ toolbaritem[cui-areatype="menu-panel"] > :-moz-any(@nestedButtons@) > .toolbarbu
|
||||
}
|
||||
|
||||
#urlbar-search-settings:hover {
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#urlbar-search-settings:hover:active {
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
#urlbar-search-splitter {
|
||||
|
@ -137,7 +137,7 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
|
||||
|
||||
.search-panel-header {
|
||||
font-weight: normal;
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border: none;
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
padding: 3px 5px;
|
||||
@ -301,7 +301,7 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
|
||||
}
|
||||
|
||||
.search-setting-button[selected] {
|
||||
background-color: hsla(210,4%,10%,.15);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
.search-setting-button-compact {
|
||||
|
@ -44,6 +44,9 @@
|
||||
--urlbar-dropmarker-active-2x-region: rect(0, 44px, 28px, 22px);
|
||||
|
||||
--panel-separator-color: hsla(210,4%,10%,.14);
|
||||
--arrowpanel-dimmed: hsla(210,4%,10%,.07);
|
||||
--arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
|
||||
--arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
|
||||
|
||||
--urlbar-separator-color: hsla(0,0%,16%,.2);
|
||||
}
|
||||
@ -1708,7 +1711,7 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
|
||||
|
||||
#urlbar-search-footer {
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#urlbar-search-settings {
|
||||
@ -1720,11 +1723,11 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
|
||||
}
|
||||
|
||||
#urlbar-search-settings:hover {
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#urlbar-search-settings:hover:active {
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
#urlbar-search-splitter {
|
||||
|
@ -127,7 +127,7 @@
|
||||
.search-panel-header {
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
margin: 0;
|
||||
padding: 3px 6px;
|
||||
@ -283,7 +283,7 @@
|
||||
}
|
||||
|
||||
.search-setting-button[selected] {
|
||||
background-color: hsla(210,4%,10%,.15);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
.search-setting-button-compact {
|
||||
|
@ -156,11 +156,11 @@
|
||||
}
|
||||
|
||||
.identity-popup-expander:hover {
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
.identity-popup-expander:hover:active {
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@
|
||||
|
||||
#identity-popup-securityView-footer {
|
||||
margin-top: 1em;
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#identity-popup-securityView-footer > button {
|
||||
@ -293,11 +293,11 @@
|
||||
|
||||
#identity-popup-securityView-footer > button:hover,
|
||||
#identity-popup-securityView-footer > button:focus {
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#identity-popup-securityView-footer > button:hover:active {
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
#identity-popup-content-verifier ~ description {
|
||||
|
@ -407,7 +407,7 @@ toolbarpaletteitem:-moz-any([place="palette"], [place="panel"]) > toolbaritem[sd
|
||||
}
|
||||
|
||||
.panelUI-grid .toolbarbutton-1:not([buttonover])@buttonStateHover@ > .toolbarbutton-menubutton-dropmarker {
|
||||
background-color: hsla(210,4%,10%,.1) !important;
|
||||
background-color: var(--arrowpanel-dimmed) !important;
|
||||
border-radius: 0 0 0 2px;
|
||||
}
|
||||
|
||||
@ -434,7 +434,7 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe {
|
||||
}
|
||||
|
||||
#PanelUI-multiView[viewtype="subview"] > .panel-viewcontainer > .panel-viewstack > .panel-mainview > #PanelUI-mainView {
|
||||
background-color: hsla(210,4%,10%,.1);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
|
||||
@ -535,7 +535,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
@ -552,7 +552,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status::after {
|
||||
content: url(chrome://browser/skin/warning.svg);
|
||||
filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
|
||||
filter: drop-shadow(0 1px 0 hsla(206,50%,10%,.15));
|
||||
width: 47px;
|
||||
padding-top: 1px;
|
||||
display: block;
|
||||
@ -566,7 +566,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 16.5px;
|
||||
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px rgba(0, 0, 0, 0.1) inset, 0px 1px 0px rgba(12, 27, 38, 0.2);
|
||||
box-shadow: 0px 1px 0px rgba(255,255,255,.2) inset, 0px -1px 0px rgba(0,0,0,.1) inset, 0px 1px 0px rgba(12,27,38,.2);
|
||||
border-radius: 2px;
|
||||
background-size: contain;
|
||||
display: -moz-box;
|
||||
@ -909,8 +909,8 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
#PanelUI-help:not([disabled]):hover,
|
||||
#PanelUI-customize:hover,
|
||||
#PanelUI-quit:not([disabled]):hover {
|
||||
outline: 1px solid hsla(210,4%,10%,.07);
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
outline: 1px solid var(--arrowpanel-dimmed);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-status:not([disabled]):hover:active,
|
||||
@ -918,8 +918,8 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
#PanelUI-help:not([disabled]):hover:active,
|
||||
#PanelUI-customize:hover:active,
|
||||
#PanelUI-quit:not([disabled]):hover:active {
|
||||
outline: 1px solid hsla(210,4%,10%,.12);
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
outline: 1px solid var(--arrowpanel-dimmed-further);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
@ -931,16 +931,16 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] {
|
||||
background-color: hsla(42, 94%, 88%, 1.0);
|
||||
border-top: 1px solid hsla(42, 94%, 70%, 1.0);
|
||||
background-color: hsl(42,94%,88%);
|
||||
border-top: 1px solid hsl(42,94%,70%);
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover {
|
||||
background-color: hsla(42, 94%, 85%, 1.0);
|
||||
background-color: hsl(42,94%,85%);
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover:active {
|
||||
background-color: hsla(42, 94%, 82%, 1.0);
|
||||
background-color: hsl(42,94%,82%);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
@ -949,27 +949,27 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status="succeeded"] {
|
||||
background-color: hsla(96, 65%, 75%, 0.5);
|
||||
background-color: hsla(96,65%,75%,.5);
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover {
|
||||
background-color: hsla(96, 65%, 75%, 0.8);
|
||||
background-color: hsla(96,65%,75%,.8);
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover:active {
|
||||
background-color: hsl(96, 65%, 75%);
|
||||
background-color: hsl(96,65%,75%);
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status="failed"] {
|
||||
background-color: hsla(359, 69%, 84%, 0.5);
|
||||
background-color: hsla(359,69%,84%,.5);
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover {
|
||||
background-color: hsla(359, 69%, 84%, 0.8);
|
||||
background-color: hsla(359,69%,84%,.8);
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover:active {
|
||||
background-color: hsl(359, 69%, 84%);
|
||||
background-color: hsl(359,69%,84%);
|
||||
}
|
||||
|
||||
#PanelUI-quit:not([disabled]):hover {
|
||||
@ -1011,10 +1011,10 @@ panelview .toolbarbutton-1,
|
||||
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
|
||||
-moz-appearance: none;
|
||||
padding: 0 6px;
|
||||
background-color: hsla(210,4%,10%,0);
|
||||
background-color: transparent;
|
||||
border-radius: 2px;
|
||||
border-style: solid;
|
||||
border-color: hsla(210,4%,10%,0);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
panelview .toolbarbutton-1,
|
||||
@ -1097,12 +1097,12 @@ menuitem.subviewbutton@menuStateHover@,
|
||||
.share-provider-button@buttonStateHover@:not([checked="true"]),
|
||||
.widget-overflow-list .toolbarbutton-1@buttonStateHover@,
|
||||
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
|
||||
background-color: hsla(210,4%,10%,.08);
|
||||
border-color: hsla(210,4%,10%,.11);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border-color: var(--panel-separator-color);
|
||||
}
|
||||
|
||||
.toolbaritem-combined-buttons@inAnyPanel@@buttonStateHover@ {
|
||||
border-color: hsla(210,4%,10%,.11);
|
||||
border-color: var(--panel-separator-color);
|
||||
}
|
||||
|
||||
panelview .toolbarbutton-1:-moz-any(@buttonStateActive@,[checked=true]),
|
||||
@ -1112,26 +1112,26 @@ menuitem.subviewbutton@menuStateActive@,
|
||||
.share-provider-button:-moz-any(@buttonStateActive@,[checked=true]),
|
||||
.widget-overflow-list .toolbarbutton-1@buttonStateActive@,
|
||||
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
border-color: var(--panel-separator-color);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
|
||||
}
|
||||
|
||||
.subviewbutton.panel-subview-footer {
|
||||
margin: 4px -4px -4px;
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
menuitem.panel-subview-footer@menuStateHover@,
|
||||
.subviewbutton.panel-subview-footer@buttonStateHover@ {
|
||||
background-color: hsla(210,4%,10%,.15);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
menuitem.panel-subview-footer@menuStateActive@,
|
||||
.subviewbutton.panel-subview-footer@buttonStateActive@ {
|
||||
background-color: hsla(210,4%,10%,.19);
|
||||
background-color: var(--arrowpanel-dimmed-even-further);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
@ -1298,11 +1298,11 @@ toolbarpaletteitem[place="palette"] > #search-container {
|
||||
}
|
||||
|
||||
.toolbaritem-combined-buttons@inAnyPanel@ {
|
||||
background-color: hsla(210,4%,10%,0);
|
||||
background-color: transparent;
|
||||
border-radius: 2px;
|
||||
border: 1px solid;
|
||||
border-color: hsla(210,4%,10%,0);
|
||||
border-bottom-color: hsla(210,4%,10%,.1);
|
||||
border-color: transparent;
|
||||
border-bottom-color: var(--panel-separator-color);
|
||||
padding: 0;
|
||||
transition-property: background-color, border-color;
|
||||
transition-duration: 150ms;
|
||||
@ -1481,7 +1481,7 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
|
||||
|
||||
#PanelUI-panic-timeframe {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.1);
|
||||
border-bottom: 1px solid var(--panel-separator-color);
|
||||
}
|
||||
|
||||
#panic-button-success-icon,
|
||||
@ -1540,14 +1540,14 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
|
||||
}
|
||||
|
||||
.subviewradio@buttonStateHover@ {
|
||||
background-color: hsla(210,4%,10%,.08);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border-color: var(--panel-separator-color);
|
||||
}
|
||||
|
||||
.subviewradio[selected],
|
||||
.subviewradio[selected]:hover,
|
||||
.subviewradio@buttonStateActive@ {
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
border-color: var(--panel-separator-color);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@
|
||||
}
|
||||
|
||||
.downloadsPanelFooter {
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
}
|
||||
|
||||
@ -65,14 +65,14 @@
|
||||
}
|
||||
|
||||
.downloadsPanelFooterButton:hover {
|
||||
outline: 1px solid hsla(210,4%,10%,.07);
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
outline: 1px solid var(--arrowpanel-dimmed);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
.downloadsPanelFooterButton:hover:active,
|
||||
.downloadsPanelFooterButton[open="true"] {
|
||||
outline: 1px solid hsla(210,4%,10%,.12);
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
outline: 1px solid var(--arrowpanel-dimmed-further);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,8 @@
|
||||
}
|
||||
|
||||
.click-to-play-plugins-notification-button-container {
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
border-top: 1px solid hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
padding: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
@ -46,6 +46,9 @@
|
||||
--urlbar-dropmarker-active-2x-region: rect(0, 66px, 28px, 44px);
|
||||
|
||||
--panel-separator-color: ThreeDLightShadow;
|
||||
--arrowpanel-dimmed: hsla(210,4%,10%,.07);
|
||||
--arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
|
||||
--arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
|
||||
|
||||
--urlbar-separator-color: ThreeDLightShadow;
|
||||
}
|
||||
@ -1444,7 +1447,7 @@ html|*.urlbar-input:-moz-lwtheme::placeholder,
|
||||
|
||||
#urlbar-search-footer {
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#urlbar-search-settings {
|
||||
@ -1458,11 +1461,11 @@ html|*.urlbar-input:-moz-lwtheme::placeholder,
|
||||
}
|
||||
|
||||
#urlbar-search-settings:hover {
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#urlbar-search-settings:hover:active {
|
||||
background-color: hsla(210,4%,10%,.12);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
#urlbar-search-splitter {
|
||||
|
@ -132,7 +132,7 @@
|
||||
|
||||
.search-panel-header {
|
||||
font-weight: normal;
|
||||
background-color: hsla(210,4%,10%,.07);
|
||||
background-color: var(--arrowpanel-dimmed);
|
||||
border: none;
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
margin: 0;
|
||||
@ -293,7 +293,7 @@
|
||||
}
|
||||
|
||||
.search-setting-button[selected] {
|
||||
background-color: hsla(210,4%,10%,.15);
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
}
|
||||
|
||||
.search-setting-button-compact {
|
||||
|
@ -2,6 +2,14 @@ dnl This Source Code Form is subject to the terms of the Mozilla Public
|
||||
dnl License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
dnl Several autoconf functions AC_REQUIRE AC_PROG_CPP/AC_PROG_CXXCPP,
|
||||
dnl meaning they are called even when we don't call them explicitly.
|
||||
dnl However, theses checks are not necessary and python configure sets
|
||||
dnl the corresponding variables already, so just skip those tests
|
||||
dnl entirely.
|
||||
define([AC_PROG_CPP],[])
|
||||
define([AC_PROG_CXXCPP],[])
|
||||
|
||||
AC_DEFUN([MOZ_TOOL_VARIABLES],
|
||||
[
|
||||
GNU_AS=
|
||||
|
@ -754,6 +754,23 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
|
||||
valid_compiler.try_compile(check_msg='%s works' % what,
|
||||
onerror=compiler_error)
|
||||
|
||||
|
||||
# Set CPP/CXXCPP for both the build system and old-configure. We don't
|
||||
# need to check this works for preprocessing, because we already relied
|
||||
# on $CC -E/$CXX -E doing preprocessing work to validate the compiler
|
||||
# in the first place.
|
||||
if host_or_target == target:
|
||||
pp_var = {
|
||||
'C': 'CPP',
|
||||
'C++': 'CXXCPP',
|
||||
}[language]
|
||||
|
||||
preprocessor = depends_if(valid_compiler)(
|
||||
lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
|
||||
|
||||
set_config(pp_var, preprocessor)
|
||||
add_old_configure_assignment(pp_var, preprocessor)
|
||||
|
||||
return valid_compiler
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@ add_task(function* () {
|
||||
webconsole: hud,
|
||||
messages: [
|
||||
{
|
||||
text: "SyntaxError: redefining arguments is deprecated",
|
||||
text: "SyntaxError: 'arguments' can't be defined or assigned to in strict mode code",
|
||||
category: CATEGORY_JS,
|
||||
severity: SEVERITY_ERROR,
|
||||
},
|
||||
|
@ -126,6 +126,22 @@ KeyframeEffect::SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffect::SetIterationComposite(
|
||||
const IterationCompositeOperation& aIterationComposite)
|
||||
{
|
||||
if (mEffectOptions.mIterationComposite == aIterationComposite) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAnimation && mAnimation->IsRelevant()) {
|
||||
nsNodeUtils::AnimationChanged(mAnimation);
|
||||
}
|
||||
|
||||
mEffectOptions.mIterationComposite = aIterationComposite;
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffect::SetSpacing(JSContext* aCx,
|
||||
const nsAString& aSpacing,
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
|
||||
|
||||
void SetSpacing(JSContext* aCx, const nsAString& aSpacing, ErrorResult& aRv);
|
||||
void SetIterationComposite(
|
||||
const IterationCompositeOperation& aIterationComposite);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -9,6 +9,11 @@
|
||||
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsString.h"
|
||||
// X11 has a #define for None
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h" // IterationCompositeOperation
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -51,8 +56,9 @@ struct KeyframeEffectParams
|
||||
nsAString& aInvalidPacedProperty,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// FIXME: Bug 1216843: Add IterationCompositeOperations and
|
||||
// Bug 1216844: Add CompositeOperation
|
||||
dom::IterationCompositeOperation mIterationComposite =
|
||||
dom::IterationCompositeOperation::Replace;
|
||||
// FIXME: Bug 1216844: Add CompositeOperation
|
||||
SpacingMode mSpacingMode = SpacingMode::distribute;
|
||||
nsCSSPropertyID mPacedProperty = eCSSProperty_UNKNOWN;
|
||||
};
|
||||
|
@ -77,7 +77,7 @@ KeyframeEffectReadOnly::WrapObject(JSContext* aCx,
|
||||
IterationCompositeOperation
|
||||
KeyframeEffectReadOnly::IterationComposite() const
|
||||
{
|
||||
return IterationCompositeOperation::Replace;
|
||||
return mEffectOptions.mIterationComposite;
|
||||
}
|
||||
|
||||
CompositeOperation
|
||||
@ -111,12 +111,7 @@ KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
|
||||
}
|
||||
|
||||
// Request restyle if necessary.
|
||||
//
|
||||
// Bug 1216843: When we implement iteration composite modes, we need to
|
||||
// also detect if the current iteration has changed.
|
||||
if (mAnimation &&
|
||||
!mProperties.IsEmpty() &&
|
||||
GetComputedTiming().mProgress != mProgressOnLastCompose) {
|
||||
if (mAnimation && !mProperties.IsEmpty() && HasComputedTimingChanged()) {
|
||||
EffectCompositor::RestyleType restyleType =
|
||||
CanThrottle() ?
|
||||
EffectCompositor::RestyleType::Throttled :
|
||||
@ -125,11 +120,13 @@ KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
|
||||
}
|
||||
|
||||
// If we're no longer "in effect", our ComposeStyle method will never be
|
||||
// called and we will never have a chance to update mProgressOnLastCompose.
|
||||
// We clear mProgressOnLastCompose here to ensure that if we later become
|
||||
// "in effect" we will request a restyle (above).
|
||||
// called and we will never have a chance to update mProgressOnLastCompose
|
||||
// and mCurrentIterationOnLastCompose.
|
||||
// We clear them here to ensure that if we later become "in effect" we will
|
||||
// request a restyle (above).
|
||||
if (!inEffect) {
|
||||
mProgressOnLastCompose.SetNull();
|
||||
mCurrentIterationOnLastCompose = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,6 +309,7 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
{
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
mProgressOnLastCompose = computedTiming.mProgress;
|
||||
mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
|
||||
|
||||
// If the progress is null, we don't have fill data for the current
|
||||
// time so we shouldn't animate.
|
||||
@ -373,12 +371,42 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
aStyleRule = new AnimValuesStyleRule();
|
||||
}
|
||||
|
||||
StyleAnimationValue fromValue = segment->mFromValue;
|
||||
StyleAnimationValue toValue = segment->mToValue;
|
||||
// Iteration composition for accumulate
|
||||
if (mEffectOptions.mIterationComposite ==
|
||||
IterationCompositeOperation::Accumulate &&
|
||||
computedTiming.mCurrentIteration > 0) {
|
||||
const AnimationPropertySegment& lastSegment =
|
||||
prop.mSegments.LastElement();
|
||||
// FIXME: Bug 1293492: Add a utility function to calculate both of
|
||||
// below StyleAnimationValues.
|
||||
DebugOnly<bool> accumulateResult =
|
||||
StyleAnimationValue::Accumulate(prop.mProperty,
|
||||
fromValue,
|
||||
lastSegment.mToValue,
|
||||
computedTiming.mCurrentIteration);
|
||||
// We can't check the accumulation result in case of filter property.
|
||||
// That's because some filter property can't accumulate,
|
||||
// e.g. 'contrast(2) brightness(2)' onto 'brightness(1) contrast(1)'
|
||||
// because of mismatch of the order.
|
||||
MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
|
||||
"could not accumulate value");
|
||||
accumulateResult =
|
||||
StyleAnimationValue::Accumulate(prop.mProperty,
|
||||
toValue,
|
||||
lastSegment.mToValue,
|
||||
computedTiming.mCurrentIteration);
|
||||
MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
|
||||
"could not accumulate value");
|
||||
}
|
||||
|
||||
// Special handling for zero-length segments
|
||||
if (segment->mToKey == segment->mFromKey) {
|
||||
if (computedTiming.mProgress.Value() < 0) {
|
||||
aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
|
||||
aStyleRule->AddValue(prop.mProperty, Move(fromValue));
|
||||
} else {
|
||||
aStyleRule->AddValue(prop.mProperty, segment->mToValue);
|
||||
aStyleRule->AddValue(prop.mProperty, Move(toValue));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -394,14 +422,14 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
MOZ_ASSERT(IsFinite(valuePosition), "Position value should be finite");
|
||||
StyleAnimationValue val;
|
||||
if (StyleAnimationValue::Interpolate(prop.mProperty,
|
||||
segment->mFromValue,
|
||||
segment->mToValue,
|
||||
fromValue,
|
||||
toValue,
|
||||
valuePosition, val)) {
|
||||
aStyleRule->AddValue(prop.mProperty, Move(val));
|
||||
} else if (valuePosition < 0.5) {
|
||||
aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
|
||||
aStyleRule->AddValue(prop.mProperty, Move(fromValue));
|
||||
} else {
|
||||
aStyleRule->AddValue(prop.mProperty, segment->mToValue);
|
||||
aStyleRule->AddValue(prop.mProperty, Move(toValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -482,6 +510,7 @@ KeyframeEffectParamsFromUnion(const OptionsType& aOptions,
|
||||
result.mPacedProperty,
|
||||
aInvalidPacedProperty,
|
||||
aRv);
|
||||
result.mIterationComposite = options.mIterationComposite;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1280,5 +1309,20 @@ KeyframeEffectReadOnly::MarkCascadeNeedsUpdate()
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::HasComputedTimingChanged() const
|
||||
{
|
||||
// Typically we don't need to request a restyle if the progress hasn't
|
||||
// changed since the last call to ComposeStyle. The one exception is if the
|
||||
// iteration composite mode is 'accumulate' and the current iteration has
|
||||
// changed, since that will often produce a different result.
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
return computedTiming.mProgress != mProgressOnLastCompose ||
|
||||
(mEffectOptions.mIterationComposite ==
|
||||
IterationCompositeOperation::Accumulate &&
|
||||
computedTiming.mCurrentIteration !=
|
||||
mCurrentIterationOnLastCompose);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -375,6 +375,11 @@ protected:
|
||||
// timing function) so we can avoid unnecessary style updates.
|
||||
Nullable<double> mProgressOnLastCompose;
|
||||
|
||||
// The purpose of this value is the same as mProgressOnLastCompose but
|
||||
// this is used to detect when the current iteration is not changing
|
||||
// in the case when iterationComposite is accumulate.
|
||||
uint64_t mCurrentIterationOnLastCompose = 0;
|
||||
|
||||
// We need to track when we go to or from being "in effect" since
|
||||
// we need to re-evaluate the cascade of animations when that changes.
|
||||
bool mInEffectOnLastAnimationTimingUpdate;
|
||||
@ -387,6 +392,10 @@ private:
|
||||
bool CanThrottle() const;
|
||||
bool CanThrottleTransformChanges(nsIFrame& aFrame) const;
|
||||
|
||||
// Returns true if the computedTiming has changed since the last
|
||||
// composition.
|
||||
bool HasComputedTimingChanged() const;
|
||||
|
||||
// Returns true unless Gecko limitations prevent performing transform
|
||||
// animations for |aFrame|. When returning true, the reason for the
|
||||
// limitation is stored in |aOutPerformanceWarning|.
|
||||
|
@ -8167,6 +8167,7 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
EventMessage msg;
|
||||
WidgetMouseEvent::ExitFrom exitFrom = WidgetMouseEvent::eChild;
|
||||
bool contextMenuKey = false;
|
||||
if (aType.EqualsLiteral("mousedown")) {
|
||||
msg = eMouseDown;
|
||||
@ -8178,6 +8179,9 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
|
||||
msg = eMouseEnterIntoWidget;
|
||||
} else if (aType.EqualsLiteral("mouseout")) {
|
||||
msg = eMouseExitFromWidget;
|
||||
} else if (aType.EqualsLiteral("mousecancel")) {
|
||||
msg = eMouseExitFromWidget;
|
||||
exitFrom = WidgetMouseEvent::eTopLevel;
|
||||
} else if (aType.EqualsLiteral("mouselongtap")) {
|
||||
msg = eMouseLongTap;
|
||||
} else if (aType.EqualsLiteral("contextmenu")) {
|
||||
@ -8209,6 +8213,7 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
|
||||
event.mClickCount = aClickCount;
|
||||
event.mTime = PR_IntervalNow();
|
||||
event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
|
||||
event.mExitFrom = exitFrom;
|
||||
|
||||
nsPresContext* presContext = aPresShell->GetPresContext();
|
||||
if (!presContext)
|
||||
|
@ -2581,6 +2581,44 @@ void
|
||||
nsJSContext::NotifyDidPaint()
|
||||
{
|
||||
sDidPaintAfterPreviousICCSlice = true;
|
||||
if (sICCTimer) {
|
||||
static uint32_t sCount = 0;
|
||||
// 16 here is the common value for refresh driver tick frequency.
|
||||
static const uint32_t kTicksPerSliceDelay = kICCIntersliceDelay / 16;
|
||||
if (++sCount % kTicksPerSliceDelay != 0) {
|
||||
// Don't trigger CC slice all the time after paint, but often still.
|
||||
// The key point is to trigger it right after paint, especially when
|
||||
// we're running RefreshDriver constantly.
|
||||
return;
|
||||
}
|
||||
|
||||
sICCTimer->Cancel();
|
||||
ICCTimerFired(nullptr, nullptr);
|
||||
if (sICCTimer) {
|
||||
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
|
||||
kICCIntersliceDelay,
|
||||
nsITimer::TYPE_REPEATING_SLACK,
|
||||
"ICCTimerFired");
|
||||
}
|
||||
} else if (sCCTimer) {
|
||||
static uint32_t sCount = 0;
|
||||
static const uint32_t kTicksPerForgetSkippableDelay =
|
||||
NS_CC_SKIPPABLE_DELAY / 16;
|
||||
if (++sCount % kTicksPerForgetSkippableDelay != 0) {
|
||||
// The comment above about triggering CC slice applies to forget skippable
|
||||
// too.
|
||||
return;
|
||||
}
|
||||
|
||||
sCCTimer->Cancel();
|
||||
CCTimerFired(nullptr, nullptr);
|
||||
if (sCCTimer) {
|
||||
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
|
||||
NS_CC_SKIPPABLE_DELAY,
|
||||
nsITimer::TYPE_REPEATING_SLACK,
|
||||
"CCTimerFired");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsScriptNameSpaceManager*
|
||||
|
@ -16,7 +16,7 @@ def generateLine(propName, extendedAttrs):
|
||||
return " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
|
||||
propName)
|
||||
def generate(output, idlFilename, preprocessorHeader):
|
||||
cpp = shellutil.split(buildconfig.substs['CPP'])
|
||||
cpp = list(buildconfig.substs['CPP'])
|
||||
cpp += shellutil.split(buildconfig.substs['ACDEFINES'])
|
||||
cpp.append(preprocessorHeader)
|
||||
preprocessed = subprocess.check_output(cpp)
|
||||
|
@ -1908,6 +1908,8 @@ CanvasRenderingContext2D::ReturnTarget(bool aForceReset)
|
||||
// balance out here. See the comment in RestoreClipsAndTransformToTarget.
|
||||
mTarget->PopClip();
|
||||
}
|
||||
|
||||
mTarget->SetTransform(Matrix());
|
||||
}
|
||||
|
||||
mBufferProvider->ReturnDrawTarget(mTarget.forget());
|
||||
|
@ -61,7 +61,6 @@ support-files =
|
||||
support-files = pointerevent_pointerleave_mouse-manual.html
|
||||
[test_pointerevent_pointerleave_pen-manual.html]
|
||||
support-files = pointerevent_pointerleave_pen-manual.html
|
||||
disabled = should be investigated
|
||||
[test_pointerevent_pointerleave_touch-manual.html]
|
||||
support-files = pointerevent_pointerleave_touch-manual.html
|
||||
[test_pointerevent_pointermove-manual.html]
|
||||
@ -80,7 +79,6 @@ support-files =
|
||||
support-files = pointerevent_pointerout_after_pointerup_nohover-manual.html
|
||||
[test_pointerevent_pointerout_pen-manual.html]
|
||||
support-files = pointerevent_pointerout_pen-manual.html
|
||||
disabled = should be investigated
|
||||
[test_pointerevent_pointerout_received_once-manual.html]
|
||||
support-files = pointerevent_pointerout_received_once-manual.html
|
||||
[test_pointerevent_pointerover-manual.html]
|
||||
|
@ -42,26 +42,6 @@ function completion_function() {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to send PointerEvent with different parameters
|
||||
function sendPointerEvent(int_win, elemId, pointerEventType, inputSource, params) {
|
||||
var elem = int_win.document.getElementById(elemId);
|
||||
if(!!elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
var eventObj = {type: pointerEventType, inputSource: inputSource};
|
||||
if(params && "button" in params)
|
||||
eventObj.button = params.button;
|
||||
if(params && "isPrimary" in params)
|
||||
eventObj.isPrimary = params.isPrimary;
|
||||
else if(MouseEvent.MOZ_SOURCE_MOUSE == inputSource)
|
||||
eventObj.isPrimary = true;
|
||||
console.log(elemId, eventObj);
|
||||
var salt = ("pointermove" == pointerEventType) ? 1 : 2;
|
||||
synthesizePointer(elem, rect.width*salt/5, rect.height/2, eventObj, int_win);
|
||||
} else {
|
||||
is(!!elem, true, "Document should have element with id: " + elemId);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to send MouseEvent with different parameters
|
||||
function sendMouseEvent(int_win, elemId, mouseEventType, params) {
|
||||
var elem = int_win.document.getElementById(elemId);
|
||||
|
@ -18,13 +18,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
iframe.src = "pointerevent_capture_mouse-manual.html";
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendPointerEvent(int_win, "btnCapture", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE, {button:1});
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointerup", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendMouseEvent(int_win, "btnCapture", "mousemove");
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1});
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
sendMouseEvent(int_win, "target1", "mouseup");
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -18,14 +18,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
iframe.src = "pointerevent_capture_suppressing_mouse-manual.html";
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE, {button:1});
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointerup", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1});
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
sendMouseEvent(int_win, "target1", "mouseup");
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -18,8 +18,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
iframe.src = "pointerevent_pointerleave_descendants-manual.html";
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "log", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "log", "mousemove");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -18,9 +18,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
iframe.src = "pointerevent_pointerleave_pen-manual.html";
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mouseleave", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -18,9 +18,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
iframe.src = "pointerevent_pointerout_pen-manual.html";
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mouseout", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -18,8 +18,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
iframe.src = "pointerevent_releasepointercapture_onpointerup_mouse-manual.html";
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "btnCapture", "pointerup", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendMouseEvent(int_win, "btnCapture", "mousedown");
|
||||
sendMouseEvent(int_win, "btnCapture", "mouseup");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -18,9 +18,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
iframe.src = "pointerevent_setpointercapture_inactive_button_mouse-manual.html";
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -1676,10 +1676,9 @@ nsGenericHTMLElement::GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, nsIURI** aU
|
||||
/* static */ bool
|
||||
nsGenericHTMLElement::IsScrollGrabAllowed(JSContext*, JSObject*)
|
||||
{
|
||||
// Only allow scroll grabbing in chrome and certified apps.
|
||||
// Only allow scroll grabbing in chrome
|
||||
nsIPrincipal* prin = nsContentUtils::SubjectPrincipal();
|
||||
return nsContentUtils::IsSystemPrincipal(prin) ||
|
||||
prin->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
|
||||
return nsContentUtils::IsSystemPrincipal(prin);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -280,8 +280,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
const long MODIFIER_OS = 0x1000;
|
||||
|
||||
/** Synthesize a mouse event. The event types supported are:
|
||||
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
|
||||
* MozMouseHittest
|
||||
* mousedown, mouseup, mousemove, mouseover, mouseout, mousecancel,
|
||||
* contextmenu, MozMouseHittest
|
||||
*
|
||||
* Events are sent in coordinates offset by aX and aY from the window.
|
||||
*
|
||||
@ -301,6 +301,11 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* window under the toplevel window, in some cases it could never reach this
|
||||
* window at all.
|
||||
*
|
||||
* NOTE: mousecancel is used to represent the vanishing of an input device
|
||||
* such as a pen leaving its digitizer by synthesizing a WidgetMouseEvent,
|
||||
* whose mMessage is eMouseExitFromWidget and mExitFrom is
|
||||
* WidgetMouseEvent::eTopLevel.
|
||||
*
|
||||
* @param aType event type
|
||||
* @param aX x offset in CSS pixels
|
||||
* @param aY y offset in CSS pixels
|
||||
|
@ -69,8 +69,7 @@ partial interface KeyframeEffectReadOnly {
|
||||
optional (unrestricted double or KeyframeEffectOptions) options)]
|
||||
interface KeyframeEffect : KeyframeEffectReadOnly {
|
||||
inherit attribute (Element or CSSPseudoElement)? target;
|
||||
// Bug 1216843 - implement animation composition
|
||||
// inherit attribute IterationCompositeOperation iterationComposite;
|
||||
inherit attribute IterationCompositeOperation iterationComposite;
|
||||
// Bug 1216844 - implement additive animation
|
||||
// inherit attribute CompositeOperation composite;
|
||||
[SetterThrows]
|
||||
|
@ -73,7 +73,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget {
|
||||
void open(ByteString method, DOMString url);
|
||||
[Throws]
|
||||
void open(ByteString method, DOMString url, boolean async,
|
||||
optional DOMString? user, optional DOMString? password);
|
||||
optional DOMString? user=null, optional DOMString? password=null);
|
||||
[Throws]
|
||||
void setRequestHeader(ByteString header, ByteString value);
|
||||
|
||||
|
@ -53,9 +53,8 @@ public:
|
||||
Open(const nsACString& aMethod, const nsAString& aUrl, ErrorResult& aRv) = 0;
|
||||
|
||||
virtual void
|
||||
Open(const nsACString& aMethod, const nsAString& aUrl,
|
||||
bool aAsync, const Optional<nsAString>& aUser,
|
||||
const Optional<nsAString>& aPassword, ErrorResult& aRv) = 0;
|
||||
Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
|
||||
const nsAString& aUser, const nsAString& aPassword, ErrorResult& aRv) = 0;
|
||||
|
||||
virtual void
|
||||
SetRequestHeader(const nsACString& aHeader, const nsACString& aValue,
|
||||
|
@ -1400,7 +1400,7 @@ XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsACString& aUrl
|
||||
if (optional_argc > 2) {
|
||||
password = &aPassword;
|
||||
}
|
||||
return OpenInternal(aMethod, aUrl, async, username, password);
|
||||
return Open(aMethod, aUrl, async, username, password);
|
||||
}
|
||||
|
||||
// This case is hit when the async parameter is outright omitted, which
|
||||
@ -1409,8 +1409,8 @@ void
|
||||
XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
aRv = OpenInternal(aMethod, NS_ConvertUTF16toUTF8(aUrl), Optional<bool>(true),
|
||||
Optional<nsAString>(), Optional<nsAString>());
|
||||
aRv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl), Optional<bool>(true),
|
||||
Optional<nsAString>(), Optional<nsAString>());
|
||||
}
|
||||
|
||||
// This case is hit when the async parameter is specified, even if the
|
||||
@ -1420,20 +1420,24 @@ void
|
||||
XMLHttpRequestMainThread::Open(const nsACString& aMethod,
|
||||
const nsAString& aUrl,
|
||||
bool aAsync,
|
||||
const Optional<nsAString>& aUsername,
|
||||
const Optional<nsAString>& aPassword,
|
||||
const nsAString& aUsername,
|
||||
const nsAString& aPassword,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
aRv = OpenInternal(aMethod, NS_ConvertUTF16toUTF8(aUrl),
|
||||
Optional<bool>(aAsync), aUsername, aPassword);
|
||||
Optional<nsAString> username;
|
||||
username = &aUsername;
|
||||
Optional<nsAString> password;
|
||||
password = &aPassword;
|
||||
aRv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl),
|
||||
Optional<bool>(aAsync), username, password);
|
||||
}
|
||||
|
||||
nsresult
|
||||
XMLHttpRequestMainThread::OpenInternal(const nsACString& aMethod,
|
||||
const nsACString& aUrl,
|
||||
const Optional<bool>& aAsync,
|
||||
const Optional<nsAString>& aUsername,
|
||||
const Optional<nsAString>& aPassword)
|
||||
XMLHttpRequestMainThread::Open(const nsACString& aMethod,
|
||||
const nsACString& aUrl,
|
||||
const Optional<bool>& aAsync,
|
||||
const Optional<nsAString>& aUsername,
|
||||
const Optional<nsAString>& aPassword)
|
||||
{
|
||||
bool async = aAsync.WasPassed() ? aAsync.Value() : true;
|
||||
|
||||
|
@ -249,10 +249,16 @@ public:
|
||||
|
||||
virtual void
|
||||
Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
|
||||
const Optional<nsAString>& aUser,
|
||||
const Optional<nsAString>& aPassword,
|
||||
const nsAString& aUsername, const nsAString& aPassword,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
nsresult
|
||||
Open(const nsACString& aMethod,
|
||||
const nsACString& aUrl,
|
||||
const Optional<bool>& aAsync,
|
||||
const Optional<nsAString>& aUsername,
|
||||
const Optional<nsAString>& aPassword);
|
||||
|
||||
virtual void
|
||||
SetRequestHeader(const nsACString& aName, const nsACString& aValue,
|
||||
ErrorResult& aRv) override
|
||||
@ -591,12 +597,6 @@ protected:
|
||||
|
||||
nsresult OnRedirectVerifyCallback(nsresult result);
|
||||
|
||||
nsresult OpenInternal(const nsACString& aMethod,
|
||||
const nsACString& aUrl,
|
||||
const Optional<bool>& aAsync,
|
||||
const Optional<nsAString>& aUsername,
|
||||
const Optional<nsAString>& aPassword);
|
||||
|
||||
already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
|
||||
|
||||
nsCOMPtr<nsISupports> mContext;
|
||||
|
@ -1426,16 +1426,15 @@ OpenRunnable::MainThreadRunInternal()
|
||||
MOZ_ASSERT(!mProxy->mInOpen);
|
||||
mProxy->mInOpen = true;
|
||||
|
||||
ErrorResult rv2;
|
||||
mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, rv2);
|
||||
rv = mProxy->mXHR->Open(mMethod, NS_ConvertUTF16toUTF8(mURL),
|
||||
Optional<bool>(true), mUser, mPassword);
|
||||
|
||||
MOZ_ASSERT(mProxy->mInOpen);
|
||||
mProxy->mInOpen = false;
|
||||
|
||||
if (rv2.Failed()) {
|
||||
return rv2.StealNSResult();
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ErrorResult rv2;
|
||||
mProxy->mXHR->SetResponseType(mResponseType, rv2);
|
||||
if (rv2.Failed()) {
|
||||
return rv2.StealNSResult();
|
||||
@ -1849,9 +1848,11 @@ XMLHttpRequestWorker::Notify(Status aStatus)
|
||||
}
|
||||
|
||||
void
|
||||
XMLHttpRequestWorker::Open(const nsACString& aMethod, const nsAString& aUrl,
|
||||
bool aAsync, const Optional<nsAString>& aUser,
|
||||
const Optional<nsAString>& aPassword, ErrorResult& aRv)
|
||||
XMLHttpRequestWorker::Open(const nsACString& aMethod,
|
||||
const nsAString& aUrl, bool aAsync,
|
||||
const Optional<nsAString>& aUser,
|
||||
const Optional<nsAString>& aPassword,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
|
@ -97,8 +97,20 @@ public:
|
||||
|
||||
virtual void
|
||||
Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
|
||||
const Optional<nsAString>& aUser, const Optional<nsAString>& aPassword,
|
||||
ErrorResult& aRv) override;
|
||||
const nsAString& aUsername, const nsAString& aPassword,
|
||||
ErrorResult& aRv) override
|
||||
{
|
||||
Optional<nsAString> username;
|
||||
username = &aUsername;
|
||||
Optional<nsAString> password;
|
||||
password = &aPassword;
|
||||
Open(aMethod, aUrl, aAsync, username, password, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
Open(const nsACString& aMethod, const nsAString& aUrl,
|
||||
bool aAsync, const Optional<nsAString>& aUser,
|
||||
const Optional<nsAString>& aPassword, ErrorResult& aRv);
|
||||
|
||||
virtual void
|
||||
SetRequestHeader(const nsACString& aHeader, const nsACString& aValue,
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
|
||||
#include "mozilla/dom/KeyframeEffectReadOnly.h"
|
||||
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
|
||||
#include "mozilla/gfx/BaseRect.h" // for BaseRect
|
||||
#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
|
||||
#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
|
||||
@ -553,18 +554,45 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoo
|
||||
}
|
||||
|
||||
static void
|
||||
SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
|
||||
StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
|
||||
SampleValue(float aPortion, Animation& aAnimation,
|
||||
const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
|
||||
const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
|
||||
Animatable* aValue, Layer* aLayer)
|
||||
{
|
||||
StyleAnimationValue interpolatedValue;
|
||||
NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
|
||||
aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
|
||||
aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
|
||||
"Must have same unit");
|
||||
|
||||
StyleAnimationValue startValue = aStart;
|
||||
StyleAnimationValue endValue = aEnd;
|
||||
// Iteration composition for accumulate
|
||||
if (static_cast<dom::IterationCompositeOperation>
|
||||
(aAnimation.iterationComposite()) ==
|
||||
dom::IterationCompositeOperation::Accumulate &&
|
||||
aCurrentIteration > 0) {
|
||||
// FIXME: Bug 1293492: Add a utility function to calculate both of
|
||||
// below StyleAnimationValues.
|
||||
DebugOnly<bool> accumulateResult =
|
||||
StyleAnimationValue::Accumulate(aAnimation.property(),
|
||||
startValue,
|
||||
aLastValue,
|
||||
aCurrentIteration);
|
||||
MOZ_ASSERT(accumulateResult, "could not accumulate value");
|
||||
accumulateResult =
|
||||
StyleAnimationValue::Accumulate(aAnimation.property(),
|
||||
endValue,
|
||||
aLastValue,
|
||||
aCurrentIteration);
|
||||
MOZ_ASSERT(accumulateResult, "could not accumulate value");
|
||||
}
|
||||
|
||||
StyleAnimationValue interpolatedValue;
|
||||
// This should never fail because we only pass transform and opacity values
|
||||
// to the compositor and they should never fail to interpolate.
|
||||
DebugOnly<bool> uncomputeResult =
|
||||
StyleAnimationValue::Interpolate(aAnimation.property(), aStart, aEnd,
|
||||
StyleAnimationValue::Interpolate(aAnimation.property(),
|
||||
startValue, endValue,
|
||||
aPortion, interpolatedValue);
|
||||
MOZ_ASSERT(uncomputeResult, "could not uncompute value");
|
||||
|
||||
@ -683,8 +711,12 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint)
|
||||
|
||||
// interpolate the property
|
||||
Animatable interpolatedValue;
|
||||
SampleValue(portion, animation, animData.mStartValues[segmentIndex],
|
||||
animData.mEndValues[segmentIndex], &interpolatedValue, layer);
|
||||
SampleValue(portion, animation,
|
||||
animData.mStartValues[segmentIndex],
|
||||
animData.mEndValues[segmentIndex],
|
||||
animData.mEndValues.LastElement(),
|
||||
computedTiming.mCurrentIteration,
|
||||
&interpolatedValue, layer);
|
||||
LayerComposite* layerComposite = layer->AsLayerComposite();
|
||||
switch (animation.property()) {
|
||||
case eCSSProperty_opacity:
|
||||
|
@ -204,6 +204,7 @@ struct Animation {
|
||||
float playbackRate;
|
||||
// This is used in the transformed progress calculation.
|
||||
TimingFunction easingFunction;
|
||||
uint8_t iterationComposite;
|
||||
};
|
||||
|
||||
// Change a layer's attributes
|
||||
|
@ -35,6 +35,16 @@ namespace detail {
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// The "generation" of a hash table is an opaque value indicating the state of
|
||||
// modification of the hash table through its lifetime. If the generation of
|
||||
// a hash table compares equal at times T1 and T2, then lookups in the hash
|
||||
// table, pointers to (or into) hash table entries, etc. at time T1 are valid
|
||||
// at time T2. If the generation compares unequal, these computations are all
|
||||
// invalid and must be performed again to be used.
|
||||
//
|
||||
// Generations are meaningfully comparable only with respect to a single hash
|
||||
// table. It's always nonsensical to compare the generation of distinct hash
|
||||
// tables H1 and H2.
|
||||
using Generation = mozilla::Opaque<uint64_t>;
|
||||
|
||||
// A JS-friendly, STL-like container providing a hash-based map from keys to
|
||||
@ -210,8 +220,6 @@ class HashMap
|
||||
return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
// If |generation()| is the same before and after a HashMap operation,
|
||||
// pointers into the table remain valid.
|
||||
Generation generation() const {
|
||||
return impl.generation();
|
||||
}
|
||||
@ -451,8 +459,6 @@ class HashSet
|
||||
return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
// If |generation()| is the same before and after a HashSet operation,
|
||||
// pointers into the table remain valid.
|
||||
Generation generation() const {
|
||||
return impl.generation();
|
||||
}
|
||||
|
@ -219,6 +219,14 @@ class JS_FRIEND_API(BaseProxyHandler)
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool canNurseryAllocate() const {
|
||||
/*
|
||||
* Nursery allocation is allowed if and only if it is safe to not
|
||||
* run |finalize| when the ProxyObject dies.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Policy enforcement methods.
|
||||
*
|
||||
* enter() allows the policy to specify whether the caller may perform |act|
|
||||
|
@ -1721,6 +1721,9 @@ struct GCPolicy<JS::Value>
|
||||
static void trace(JSTracer* trc, Value* v, const char* name) {
|
||||
js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
|
||||
}
|
||||
static bool isTenured(const Value& thing) {
|
||||
return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
@ -6966,20 +6966,15 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
|
||||
tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
|
||||
*line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
|
||||
|
||||
RootedPropertyName name(m.cx());
|
||||
|
||||
TokenKind tk;
|
||||
if (!tokenStream.getToken(&tk, TokenStream::Operand))
|
||||
return false;
|
||||
if (tk == TOK_NAME) {
|
||||
name = tokenStream.currentName();
|
||||
} else if (tk == TOK_YIELD) {
|
||||
if (!m.parser().checkYieldNameValidity())
|
||||
return false;
|
||||
name = m.cx()->names().yield;
|
||||
} else {
|
||||
if (tk != TOK_NAME && tk != TOK_YIELD)
|
||||
return false; // The regular parser will throw a SyntaxError, no need to m.fail.
|
||||
}
|
||||
|
||||
RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName));
|
||||
if (!name)
|
||||
return false;
|
||||
|
||||
ParseNode* fn = m.parser().handler.newFunctionDefinition();
|
||||
if (!fn)
|
||||
|
@ -231,7 +231,6 @@ CodeSegment::create(JSContext* cx,
|
||||
cs->interruptCode_ = codeBase + linkData.interruptOffset;
|
||||
cs->outOfBoundsCode_ = codeBase + linkData.outOfBoundsOffset;
|
||||
cs->unalignedAccessCode_ = codeBase + linkData.unalignedAccessOffset;
|
||||
cs->badIndirectCallCode_ = codeBase + linkData.badIndirectCallOffset;
|
||||
|
||||
{
|
||||
JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
|
||||
|
@ -54,7 +54,6 @@ class CodeSegment
|
||||
// These are pointers into code for stubs used for asynchronous
|
||||
// signal-handler control-flow transfer.
|
||||
uint8_t* interruptCode_;
|
||||
uint8_t* badIndirectCallCode_;
|
||||
uint8_t* outOfBoundsCode_;
|
||||
uint8_t* unalignedAccessCode_;
|
||||
|
||||
@ -84,7 +83,6 @@ class CodeSegment
|
||||
uint32_t totalLength() const { return codeLength_ + globalDataLength_; }
|
||||
|
||||
uint8_t* interruptCode() const { return interruptCode_; }
|
||||
uint8_t* badIndirectCallCode() const { return badIndirectCallCode_; }
|
||||
uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
|
||||
uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
|
||||
|
||||
|
@ -385,12 +385,12 @@ wasm::GenerateFunctionPrologue(MacroAssembler& masm, unsigned framePushed, const
|
||||
Register scratch = WasmTableCallScratchReg;
|
||||
masm.loadWasmGlobalPtr(sigId.globalDataOffset(), scratch);
|
||||
masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, scratch,
|
||||
JumpTarget::BadIndirectCall);
|
||||
JumpTarget::IndirectCallBadSig);
|
||||
break;
|
||||
}
|
||||
case SigIdDesc::Kind::Immediate:
|
||||
masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, Imm32(sigId.immediate()),
|
||||
JumpTarget::BadIndirectCall);
|
||||
JumpTarget::IndirectCallBadSig);
|
||||
break;
|
||||
case SigIdDesc::Kind::None:
|
||||
break;
|
||||
|
@ -508,7 +508,6 @@ ModuleGenerator::finishCodegen()
|
||||
linkData_.interruptOffset = interruptExit.begin;
|
||||
linkData_.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin;
|
||||
linkData_.unalignedAccessOffset = jumpTargets[JumpTarget::UnalignedAccess].begin;
|
||||
linkData_.badIndirectCallOffset = jumpTargets[JumpTarget::BadIndirectCall].begin;
|
||||
|
||||
// Only call convertOutOfRangeBranchesToThunks after all other codegen that may
|
||||
// emit new jumps to JumpTargets has finished.
|
||||
|
@ -561,6 +561,12 @@ ReadCustomDoubleNaNObject(JSContext* cx, HandleValue v, double* ret)
|
||||
return true;
|
||||
}
|
||||
|
||||
WasmInstanceObject*
|
||||
Instance::objectUnbarriered() const
|
||||
{
|
||||
return object_.unbarrieredGet();
|
||||
}
|
||||
|
||||
WasmInstanceObject*
|
||||
Instance::object() const
|
||||
{
|
||||
@ -843,7 +849,7 @@ Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
||||
}
|
||||
|
||||
for (const SharedTable& table : tables_) {
|
||||
if (!table->isTypedFunction() || !table->initialized())
|
||||
if (!table->isTypedFunction())
|
||||
continue;
|
||||
|
||||
// This logic will have to be generalized to match the import logic
|
||||
@ -853,8 +859,10 @@ Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
||||
|
||||
void** array = table->internalArray();
|
||||
uint32_t length = table->length();
|
||||
for (size_t i = 0; i < length; i++)
|
||||
UpdateEntry(*code_, newProfilingEnabled, &array[i]);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (array[i])
|
||||
UpdateEntry(*code_, newProfilingEnabled, &array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -69,6 +69,10 @@ class Instance
|
||||
friend class js::WasmMemoryObject;
|
||||
void onMovingGrow(uint8_t* prevMemoryBase);
|
||||
|
||||
// Called by WasmTableObject to barrier table writes.
|
||||
friend class Table;
|
||||
WasmInstanceObject* objectUnbarriered() const;
|
||||
|
||||
public:
|
||||
Instance(JSContext* cx,
|
||||
HandleWasmInstanceObject object,
|
||||
|
@ -534,6 +534,8 @@ WasmInstanceObject::create(JSContext* cx,
|
||||
obj->setReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
|
||||
MOZ_ASSERT(obj->isNewborn());
|
||||
|
||||
MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
|
||||
|
||||
// Root the Instance via WasmInstanceObject before any possible GC.
|
||||
auto* instance = cx->new_<Instance>(cx,
|
||||
obj,
|
||||
@ -1200,24 +1202,19 @@ WasmTableObject::getImpl(JSContext* cx, const CallArgs& args)
|
||||
uint32_t index = uint32_t(indexDbl);
|
||||
MOZ_ASSERT(double(index) == indexDbl);
|
||||
|
||||
if (!table.initialized()) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
ExternalTableElem& elem = table.externalArray()[index];
|
||||
Instance& instance = *elem.tls->instance;
|
||||
const CodeRange* codeRange = instance.code().lookupRange(elem.code);
|
||||
|
||||
// A non-function code range means the bad-indirect-call stub, so a null element.
|
||||
if (!codeRange || !codeRange->isFunction()) {
|
||||
if (!elem.code) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
Instance& instance = *elem.tls->instance;
|
||||
const CodeRange& codeRange = *instance.code().lookupRange(elem.code);
|
||||
MOZ_ASSERT(codeRange.isFunction());
|
||||
|
||||
RootedWasmInstanceObject instanceObj(cx, instance.object());
|
||||
RootedFunction fun(cx);
|
||||
if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange->funcDefIndex(), &fun))
|
||||
if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange.funcDefIndex(), &fun))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*fun);
|
||||
@ -1258,15 +1255,6 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.initialized()) {
|
||||
if (!value) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
table.init(ExportedFunctionToInstance(value));
|
||||
}
|
||||
|
||||
if (value) {
|
||||
RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
|
||||
uint32_t funcDefIndex = ExportedFunctionToDefinitionIndex(value);
|
||||
|
@ -462,13 +462,6 @@ Module::initSegments(JSContext* cx,
|
||||
MOZ_ASSERT(dataSegments_.empty());
|
||||
}
|
||||
|
||||
// Ensure all tables are initialized before storing into them.
|
||||
|
||||
for (const SharedTable& table : tables) {
|
||||
if (!table->initialized())
|
||||
table->init(instance);
|
||||
}
|
||||
|
||||
// Now that initialization can't fail partway through, write data/elem
|
||||
// segments into memories/tables.
|
||||
|
||||
|
@ -51,7 +51,6 @@ struct LinkDataCacheablePod
|
||||
uint32_t interruptOffset;
|
||||
uint32_t outOfBoundsOffset;
|
||||
uint32_t unalignedAccessOffset;
|
||||
uint32_t badIndirectCallOffset;
|
||||
|
||||
LinkDataCacheablePod() { mozilla::PodZero(this); }
|
||||
};
|
||||
|
@ -916,7 +916,8 @@ wasm::GenerateJumpTarget(MacroAssembler& masm, JumpTarget target)
|
||||
return GenerateStackOverflow(masm);
|
||||
case JumpTarget::Throw:
|
||||
return GenerateThrow(masm);
|
||||
case JumpTarget::BadIndirectCall:
|
||||
case JumpTarget::IndirectCallToNull:
|
||||
case JumpTarget::IndirectCallBadSig:
|
||||
case JumpTarget::OutOfBounds:
|
||||
case JumpTarget::UnalignedAccess:
|
||||
case JumpTarget::Unreachable:
|
||||
|
@ -48,32 +48,10 @@ Table::create(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeO
|
||||
table->array_.reset((uint8_t*)array);
|
||||
table->kind_ = desc.kind;
|
||||
table->length_ = desc.initial;
|
||||
table->initialized_ = false;
|
||||
table->external_ = desc.external;
|
||||
return table;
|
||||
}
|
||||
|
||||
void
|
||||
Table::init(Instance& instance)
|
||||
{
|
||||
MOZ_ASSERT(!initialized());
|
||||
initialized_ = true;
|
||||
|
||||
void* code = instance.codeSegment().badIndirectCallCode();
|
||||
if (external_) {
|
||||
ExternalTableElem* array = externalArray();
|
||||
TlsData* tls = &instance.tlsData();
|
||||
for (uint32_t i = 0; i < length_; i++) {
|
||||
array[i].code = code;
|
||||
array[i].tls = tls;
|
||||
}
|
||||
} else {
|
||||
void** array = internalArray();
|
||||
for (uint32_t i = 0; i < length_; i++)
|
||||
array[i] = code;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Table::tracePrivate(JSTracer* trc)
|
||||
{
|
||||
@ -87,12 +65,15 @@ Table::tracePrivate(JSTracer* trc)
|
||||
TraceEdge(trc, &maybeObject_, "wasm table object");
|
||||
}
|
||||
|
||||
if (!initialized_ || !external_)
|
||||
return;
|
||||
|
||||
ExternalTableElem* array = externalArray();
|
||||
for (uint32_t i = 0; i < length_; i++)
|
||||
array[i].tls->instance->trace(trc);
|
||||
if (external_) {
|
||||
ExternalTableElem* array = externalArray();
|
||||
for (uint32_t i = 0; i < length_; i++) {
|
||||
if (array[i].tls)
|
||||
array[i].tls->instance->trace(trc);
|
||||
else
|
||||
MOZ_ASSERT(!array[i].code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -112,7 +93,6 @@ Table::trace(JSTracer* trc)
|
||||
void**
|
||||
Table::internalArray() const
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
MOZ_ASSERT(!external_);
|
||||
return (void**)array_.get();
|
||||
}
|
||||
@ -120,7 +100,6 @@ Table::internalArray() const
|
||||
ExternalTableElem*
|
||||
Table::externalArray() const
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
MOZ_ASSERT(external_);
|
||||
return (ExternalTableElem*)array_.get();
|
||||
}
|
||||
@ -130,8 +109,13 @@ Table::set(uint32_t index, void* code, Instance& instance)
|
||||
{
|
||||
if (external_) {
|
||||
ExternalTableElem& elem = externalArray()[index];
|
||||
if (elem.tls)
|
||||
JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
|
||||
|
||||
elem.code = code;
|
||||
elem.tls = &instance.tlsData();
|
||||
|
||||
MOZ_ASSERT(elem.tls->instance->objectUnbarriered()->isTenured(), "no writeBarrierPost");
|
||||
} else {
|
||||
internalArray()[index] = code;
|
||||
}
|
||||
@ -142,7 +126,11 @@ Table::setNull(uint32_t index)
|
||||
{
|
||||
// Only external tables can set elements to null after initialization.
|
||||
ExternalTableElem& elem = externalArray()[index];
|
||||
elem.code = elem.tls->instance->codeSegment().badIndirectCallCode();
|
||||
if (elem.tls)
|
||||
JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
|
||||
|
||||
elem.code = nullptr;
|
||||
elem.tls = nullptr;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
@ -36,7 +36,6 @@ class Table : public ShareableBase<Table>
|
||||
UniqueByteArray array_;
|
||||
TableKind kind_;
|
||||
uint32_t length_;
|
||||
bool initialized_;
|
||||
bool external_;
|
||||
|
||||
void tracePrivate(JSTracer* trc);
|
||||
@ -47,21 +46,14 @@ class Table : public ShareableBase<Table>
|
||||
HandleWasmTableObject maybeObject);
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
// These accessors may be used before initialization.
|
||||
|
||||
bool external() const { return external_; }
|
||||
bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; }
|
||||
uint32_t length() const { return length_; }
|
||||
uint8_t* base() const { return array_.get(); }
|
||||
|
||||
// A Table must be initialized before any dependent instance can execute.
|
||||
|
||||
bool initialized() const { return initialized_; }
|
||||
void init(Instance& instance);
|
||||
|
||||
// After initialization, elements must be accessed. All updates must go
|
||||
// through a set() function with the exception of (profiling) updates to the
|
||||
// callee pointer that do not change which logical function is being called.
|
||||
// All updates must go through a set() function with the exception of
|
||||
// (profiling) updates to the callee pointer that do not change which
|
||||
// logical function is being called.
|
||||
|
||||
void** internalArray() const;
|
||||
ExternalTableElem* externalArray() const;
|
||||
|
@ -117,8 +117,11 @@ HandleTrap(int32_t trapIndex)
|
||||
case Trap::IntegerDivideByZero:
|
||||
errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
|
||||
break;
|
||||
case Trap::BadIndirectCall:
|
||||
errorNumber = JSMSG_WASM_BAD_IND_CALL;
|
||||
case Trap::IndirectCallToNull:
|
||||
errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
|
||||
break;
|
||||
case Trap::IndirectCallBadSig:
|
||||
errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
|
||||
break;
|
||||
case Trap::ImpreciseSimdConversion:
|
||||
errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
|
||||
|
@ -875,8 +875,10 @@ enum class Trap
|
||||
OutOfBounds,
|
||||
// Unaligned memory access.
|
||||
UnalignedAccess,
|
||||
// Bad signature for an indirect call.
|
||||
BadIndirectCall,
|
||||
// call_indirect to null.
|
||||
IndirectCallToNull,
|
||||
// call_indirect signature mismatch.
|
||||
IndirectCallBadSig,
|
||||
|
||||
// (asm.js only) SIMD float to int conversion failed because the input
|
||||
// wasn't in bounds.
|
||||
@ -899,7 +901,8 @@ enum class JumpTarget
|
||||
IntegerDivideByZero = unsigned(Trap::IntegerDivideByZero),
|
||||
OutOfBounds = unsigned(Trap::OutOfBounds),
|
||||
UnalignedAccess = unsigned(Trap::UnalignedAccess),
|
||||
BadIndirectCall = unsigned(Trap::BadIndirectCall),
|
||||
IndirectCallToNull = unsigned(Trap::IndirectCallToNull),
|
||||
IndirectCallBadSig = unsigned(Trap::IndirectCallBadSig),
|
||||
ImpreciseSimdConversion = unsigned(Trap::ImpreciseSimdConversion),
|
||||
// Non-traps
|
||||
StackOverflow,
|
||||
|
@ -717,6 +717,8 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
|
||||
|
||||
// Steps 3-7.
|
||||
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx, usedProto, wrappedProto));
|
||||
if (!promise)
|
||||
return nullptr;
|
||||
|
||||
RootedValue promiseVal(cx, ObjectValue(*promise));
|
||||
if (wrappedProto && !cx->compartment()->wrap(cx, &promiseVal))
|
||||
|
@ -24,8 +24,8 @@
|
||||
using namespace js;
|
||||
using namespace js::unicode;
|
||||
|
||||
using mozilla::CheckedInt;
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::CheckedInt;
|
||||
using mozilla::Maybe;
|
||||
|
||||
/*
|
||||
@ -187,8 +187,18 @@ enum RegExpSharedUse {
|
||||
|
||||
/*
|
||||
* ES 2016 draft Mar 25, 2016 21.2.3.2.2.
|
||||
* Because this function only ever returns |obj| in the spec, provided by the
|
||||
* user, we omit it and just return the usual success/failure.
|
||||
*
|
||||
* Steps 14-15 set |obj|'s "lastIndex" property to zero. Some of
|
||||
* RegExpInitialize's callers have a fresh RegExp not yet exposed to script:
|
||||
* in these cases zeroing "lastIndex" is infallible. But others have a RegExp
|
||||
* whose "lastIndex" property might have been made non-writable: here, zeroing
|
||||
* "lastIndex" can fail. We efficiently solve this problem by completely
|
||||
* removing "lastIndex" zeroing from the provided function.
|
||||
*
|
||||
* CALLERS MUST HANDLE "lastIndex" ZEROING THEMSELVES!
|
||||
*
|
||||
* Because this function only ever returns a user-provided |obj| in the spec,
|
||||
* we omit it and just return the usual success/failure.
|
||||
*/
|
||||
static bool
|
||||
RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
|
||||
@ -347,6 +357,10 @@ regexp_compile_impl(JSContext* cx, const CallArgs& args)
|
||||
return false;
|
||||
}
|
||||
|
||||
// The final niggling bit of step 5.
|
||||
//
|
||||
// |regexp| is user-exposed, but if its "lastIndex" property hasn't been
|
||||
// made non-writable, we can still use a fast path to zero it.
|
||||
if (regexp->lookupPure(cx->names().lastIndex)->writable()) {
|
||||
regexp->zeroLastIndex(cx);
|
||||
} else {
|
||||
|
76
js/src/devtools/octane-csv.sh
Executable file
76
js/src/devtools/octane-csv.sh
Executable file
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
function echo_to_stderr {
|
||||
echo "$1" 1>&2
|
||||
}
|
||||
|
||||
function usage_and_exit {
|
||||
echo_to_stderr "Usage:"
|
||||
echo_to_stderr " $0 <path-to-js> <number-of-iterations>"
|
||||
echo_to_stderr
|
||||
echo_to_stderr "Run octane <number-of-iterations> times, and aggregate the results"
|
||||
echo_to_stderr "into one CSV file, which is written to stdout."
|
||||
echo_to_stderr
|
||||
echo_to_stderr "See the js/src/devtools/plot-octane.R script for plotting the"
|
||||
echo_to_stderr "results."
|
||||
echo_to_stderr
|
||||
echo_to_stderr "Complete example usage with plotting:"
|
||||
echo_to_stderr
|
||||
echo_to_stderr " \$ ./js/src/devtools/octane-csv.sh path/to/js 20 > control.csv"
|
||||
echo_to_stderr
|
||||
echo_to_stderr " Next, apply some patch you'd like to test."
|
||||
echo_to_stderr
|
||||
echo_to_stderr " \$ ./js/src/devtools/octane-csv.sh path/to/js 20 > variable.csv"
|
||||
echo_to_stderr " \$ ./js/src/devtools/plot-octane.R control.csv variable.csv"
|
||||
echo_to_stderr
|
||||
echo_to_stderr " Open Rplots.pdf to view the results."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ "$#" != "2" ]]; then
|
||||
usage_and_exit
|
||||
fi
|
||||
|
||||
# Get the absolute, normalized $JS path, and ensure its an executable.
|
||||
|
||||
JS_DIR=$(dirname $1)
|
||||
if [[ ! -d "$JS_DIR" ]]; then
|
||||
echo_to_stderr "error: no such directory $JS_DIR"
|
||||
echo_to_stderr
|
||||
usage_and_exit
|
||||
fi
|
||||
|
||||
JS=$(basename $1)
|
||||
cd "$JS_DIR" > /dev/null
|
||||
JS="$(pwd)/$JS"
|
||||
if [[ ! -e "$JS" ]]; then
|
||||
echo_to_stderr "error: '$JS' is not executable"
|
||||
echo_to_stderr
|
||||
usage_and_exit
|
||||
fi
|
||||
cd - > /dev/null
|
||||
|
||||
# Go to the js/src/octane directory.
|
||||
|
||||
cd $(dirname $0)/../octane > /dev/null
|
||||
|
||||
# Run octane and transform the results into CSV.
|
||||
#
|
||||
# Run once as a warm up, and to grab the column headers. Then run the benchmark
|
||||
# $ITERS times, grabbing just the data rows.
|
||||
|
||||
echo_to_stderr "Warm up"
|
||||
"$JS" ./run.js | grep -v -- "----" | cut -f 1 -d ':' | tr '\n' ','
|
||||
echo
|
||||
|
||||
ITERS=$2
|
||||
while [[ "$ITERS" -ge "1" ]]; do
|
||||
echo_to_stderr "Iterations left: $ITERS"
|
||||
"$JS" ./run.js | grep -v -- "----" | cut -f 2 -d ':' | tr '\n' ','
|
||||
echo
|
||||
ITERS=$((ITERS - 1))
|
||||
done
|
||||
|
||||
echo_to_stderr "All done :)"
|
38
js/src/devtools/plot-octane.R
Executable file
38
js/src/devtools/plot-octane.R
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env Rscript
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# octane.R control.csv variable.csv
|
||||
#
|
||||
# Output will be placed in Rplots.pdf
|
||||
#
|
||||
# Remember: on Octane, higher is better!
|
||||
|
||||
library(ggplot2)
|
||||
|
||||
args <- commandArgs(trailingOnly = TRUE)
|
||||
|
||||
# Reading in data.
|
||||
control <- read.table(args[1], sep=",", header=TRUE)
|
||||
variable <- read.table(args[2], sep=",", header=TRUE)
|
||||
|
||||
# Pulling out columns that we want to plot.
|
||||
# Not totally necessary.
|
||||
ctrl <- control$Score..version.9.
|
||||
var <- variable$Score..version.9.
|
||||
|
||||
# Concatenating the values we want to plot.
|
||||
score <- c(ctrl, var)
|
||||
# Creating a vector of labels for the data points.
|
||||
label <- c(rep("control", length(ctrl)), rep("variable", length(var)))
|
||||
|
||||
# Creating a data frame of the score and label.
|
||||
data <- data.frame(label, score)
|
||||
|
||||
# Now plotting!
|
||||
ggplot(data, aes(label, score, color=label, pch=label)) +
|
||||
# Adding boxplot without the outliers.
|
||||
geom_boxplot(outlier.shape=NA) +
|
||||
# Adding jitter plot on top of the boxplot. If you want to spread the points
|
||||
# more, increase jitter.
|
||||
geom_jitter(position=position_jitter(width=0.05))
|
@ -845,7 +845,12 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos)
|
||||
if (!pc->sc()->needStrictChecks())
|
||||
return true;
|
||||
|
||||
if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
|
||||
if (name == context->names().eval ||
|
||||
name == context->names().arguments ||
|
||||
name == context->names().let ||
|
||||
name == context->names().static_ ||
|
||||
IsKeyword(name))
|
||||
{
|
||||
JSAutoByteString bytes;
|
||||
if (!AtomToPrintableString(context, name, &bytes))
|
||||
return false;
|
||||
@ -2639,13 +2644,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity())
|
||||
return false;
|
||||
MOZ_ASSERT(yieldHandling == YieldIsName);
|
||||
goto TOK_NAME;
|
||||
|
||||
case TOK_TRIPLEDOT: {
|
||||
case TOK_TRIPLEDOT:
|
||||
if (IsSetterKind(kind)) {
|
||||
report(ParseError, false, null(),
|
||||
JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
|
||||
@ -2655,18 +2654,6 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||
hasRest = true;
|
||||
funbox->function()->setHasRest();
|
||||
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return false;
|
||||
// FIXME: This fails to handle a rest parameter named |yield|
|
||||
// correctly outside of generators: that is,
|
||||
// |var f = (...yield) => 42;| should be valid code!
|
||||
// When this is fixed, make sure to consult both
|
||||
// |yieldHandling| and |checkYieldNameValidity| for
|
||||
// correctness until legacy generator syntax is removed.
|
||||
if (tt != TOK_NAME) {
|
||||
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
|
||||
return false;
|
||||
}
|
||||
disallowDuplicateParams = true;
|
||||
if (duplicatedParam) {
|
||||
// Has duplicated args before the rest parameter.
|
||||
@ -2674,15 +2661,23 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||
return false;
|
||||
}
|
||||
|
||||
goto TOK_NAME;
|
||||
}
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return false;
|
||||
if (tt != TOK_NAME && tt != TOK_YIELD) {
|
||||
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
|
||||
return false;
|
||||
}
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
TOK_NAME:
|
||||
case TOK_NAME: {
|
||||
case TOK_NAME:
|
||||
case TOK_YIELD: {
|
||||
if (parenFreeArrow)
|
||||
funbox->setStart(tokenStream);
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
|
||||
if (!name)
|
||||
return false;
|
||||
|
||||
if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
|
||||
&duplicatedParam))
|
||||
{
|
||||
@ -3341,19 +3336,6 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkYieldNameValidity()
|
||||
{
|
||||
// In star generators and in JS >= 1.7, yield is a keyword. Otherwise in
|
||||
// strict mode, yield is a future reserved word.
|
||||
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc()->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling)
|
||||
@ -3387,12 +3369,10 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
|
||||
return null();
|
||||
}
|
||||
|
||||
if (tt == TOK_NAME) {
|
||||
name = tokenStream.currentName();
|
||||
} else if (tt == TOK_YIELD) {
|
||||
if (!checkYieldNameValidity())
|
||||
if (tt == TOK_NAME || tt == TOK_YIELD) {
|
||||
name = bindingIdentifier(yieldHandling);
|
||||
if (!name)
|
||||
return null();
|
||||
name = tokenStream.currentName();
|
||||
} else if (defaultHandling == AllowDefaultName) {
|
||||
name = context->names().starDefaultStar;
|
||||
tokenStream.ungetToken();
|
||||
@ -3436,12 +3416,10 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
|
||||
}
|
||||
|
||||
RootedPropertyName name(context);
|
||||
if (tt == TOK_NAME) {
|
||||
name = tokenStream.currentName();
|
||||
} else if (tt == TOK_YIELD) {
|
||||
if (!checkYieldNameValidity())
|
||||
if (tt == TOK_NAME || tt == TOK_YIELD) {
|
||||
name = bindingIdentifier(YieldIsName);
|
||||
if (!name)
|
||||
return null();
|
||||
name = tokenStream.currentName();
|
||||
} else {
|
||||
tokenStream.ungetToken();
|
||||
}
|
||||
@ -3688,19 +3666,12 @@ Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<Prop
|
||||
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
|
||||
return false;
|
||||
|
||||
if (tt == TOK_NAME) {
|
||||
tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
|
||||
MOZ_ASSERT_IF(tokenStream.currentName() == context->names().yield,
|
||||
yieldHandling == YieldIsName);
|
||||
label.set(tokenStream.currentName());
|
||||
} else if (tt == TOK_YIELD) {
|
||||
// We might still consider |yield| to be valid here, contrary to ES6.
|
||||
// Fix bug 1104014, then stop shipping legacy generators in chrome
|
||||
// code, then remove this check!
|
||||
tokenStream.consumeKnownToken(TOK_YIELD, TokenStream::Operand);
|
||||
if (!checkYieldNameValidity())
|
||||
if (tt == TOK_NAME || tt == TOK_YIELD) {
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
|
||||
label.set(labelIdentifier(yieldHandling));
|
||||
if (!label)
|
||||
return false;
|
||||
label.set(tokenStream.currentName());
|
||||
} else {
|
||||
label.set(nullptr);
|
||||
}
|
||||
@ -4200,19 +4171,16 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token
|
||||
bool initialDeclaration, YieldHandling yieldHandling,
|
||||
ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
|
||||
{
|
||||
if (tt != TOK_NAME) {
|
||||
// Anything other than TOK_YIELD or TOK_NAME is an error.
|
||||
if (tt != TOK_YIELD) {
|
||||
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
}
|
||||
|
||||
// TOK_YIELD is only okay if it's treated as a name.
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
// Anything other than TOK_YIELD or TOK_NAME is an error.
|
||||
if (tt != TOK_NAME && tt != TOK_YIELD) {
|
||||
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
}
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
Node binding = newName(name);
|
||||
if (!binding)
|
||||
return null();
|
||||
@ -4385,38 +4353,58 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
|
||||
// peekToken matched it as a TOK_NAME, and put it in the
|
||||
// lookahead buffer, so this call will match keywords as well.
|
||||
MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_IMPORT_NAME);
|
||||
Node importName = newName(tokenStream.currentName());
|
||||
if (!importName)
|
||||
return false;
|
||||
Rooted<PropertyName*> importName(context, tokenStream.currentName());
|
||||
TokenPos importNamePos = pos();
|
||||
|
||||
bool foundAs;
|
||||
if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as))
|
||||
return false;
|
||||
TokenKind maybeAs;
|
||||
if (!tokenStream.peekToken(&maybeAs))
|
||||
return null();
|
||||
|
||||
if (foundAs) {
|
||||
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
|
||||
if (maybeAs == TOK_NAME &&
|
||||
tokenStream.nextName() == context->names().as)
|
||||
{
|
||||
tokenStream.consumeKnownToken(TOK_NAME);
|
||||
|
||||
if (!checkUnescapedName())
|
||||
return false;
|
||||
|
||||
TokenKind afterAs;
|
||||
if (!tokenStream.getToken(&afterAs))
|
||||
return false;
|
||||
|
||||
if (afterAs != TOK_NAME && afterAs != TOK_YIELD) {
|
||||
report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Keywords cannot be bound to themselves, so an import name
|
||||
// that is a keyword is a syntax error if it is not followed
|
||||
// by the keyword 'as'.
|
||||
// See the ImportSpecifier production in ES6 section 15.2.2.
|
||||
if (IsKeyword(importName->name())) {
|
||||
if (IsKeyword(importName)) {
|
||||
JSAutoByteString bytes;
|
||||
if (!AtomToPrintableString(context, importName->name(), &bytes))
|
||||
if (!AtomToPrintableString(context, importName, &bytes))
|
||||
return false;
|
||||
report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RootedPropertyName bindingAtom(context, tokenStream.currentName());
|
||||
RootedPropertyName bindingAtom(context, importedBinding());
|
||||
if (!bindingAtom)
|
||||
return false;
|
||||
|
||||
Node bindingName = newName(bindingAtom);
|
||||
if (!bindingName)
|
||||
return false;
|
||||
if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
|
||||
return false;
|
||||
|
||||
Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
|
||||
Node importNameNode = newName(importName, importNamePos);
|
||||
if (!importNameNode)
|
||||
return false;
|
||||
|
||||
Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importNameNode, bindingName);
|
||||
if (!importSpec)
|
||||
return false;
|
||||
|
||||
@ -4456,7 +4444,9 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
|
||||
// definitions that hold a module namespace object. They are treated
|
||||
// as const variables which are initialized during the
|
||||
// ModuleDeclarationInstantiation step.
|
||||
RootedPropertyName bindingName(context, tokenStream.currentName());
|
||||
RootedPropertyName bindingName(context, importedBinding());
|
||||
if (!bindingName)
|
||||
return false;
|
||||
Node bindingNameNode = newName(bindingName);
|
||||
if (!bindingNameNode)
|
||||
return false;
|
||||
@ -4515,10 +4505,14 @@ Parser<FullParseHandler>::importDeclaration()
|
||||
if (!importName)
|
||||
return null();
|
||||
|
||||
RootedPropertyName bindingAtom(context, tokenStream.currentName());
|
||||
RootedPropertyName bindingAtom(context, importedBinding());
|
||||
if (!bindingAtom)
|
||||
return null();
|
||||
|
||||
Node bindingName = newName(bindingAtom);
|
||||
if (!bindingName)
|
||||
return null();
|
||||
|
||||
if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
|
||||
return null();
|
||||
|
||||
@ -4870,15 +4864,28 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
return node;
|
||||
}
|
||||
|
||||
case TOK_LET:
|
||||
case TOK_CONST:
|
||||
kid = lexicalDeclaration(YieldIsName, tt == TOK_CONST);
|
||||
kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
|
||||
if (!kid)
|
||||
return null();
|
||||
if (!checkExportedNamesForDeclaration(kid))
|
||||
return null();
|
||||
break;
|
||||
|
||||
case TOK_NAME:
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
if (!checkUnescapedName())
|
||||
return null();
|
||||
|
||||
kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
|
||||
if (!kid)
|
||||
return null();
|
||||
if (!checkExportedNamesForDeclaration(kid))
|
||||
return null();
|
||||
break;
|
||||
}
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
|
||||
return null();
|
||||
@ -5124,16 +5131,15 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
|
||||
// For-in loop backwards compatibility requires that |let| starting a
|
||||
// for-loop that's not a (new to ES6) for-of loop, in non-strict mode code,
|
||||
// parse as an identifier. (|let| in for-of is always a declaration.)
|
||||
// Thus we must can't just sniff out TOK_CONST/TOK_LET here. :-(
|
||||
bool parsingLexicalDeclaration = false;
|
||||
bool letIsIdentifier = false;
|
||||
if (tt == TOK_LET || tt == TOK_CONST) {
|
||||
if (tt == TOK_CONST) {
|
||||
parsingLexicalDeclaration = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
} else if (tt == TOK_NAME && tokenStream.nextName() == context->names().let) {
|
||||
MOZ_ASSERT(!pc->sc()->strict(),
|
||||
"should parse |let| as TOK_LET in strict mode code");
|
||||
|
||||
} else if (tt == TOK_NAME &&
|
||||
tokenStream.nextName() == context->names().let &&
|
||||
!tokenStream.nextNameContainsEscape())
|
||||
{
|
||||
// We could have a {For,Lexical}Declaration, or we could have a
|
||||
// LeftHandSideExpression with lookahead restrictions so it's not
|
||||
// ambiguous with the former. Check for a continuation of the former
|
||||
@ -5875,8 +5881,9 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
|
||||
{
|
||||
uint32_t begin = pos().begin;
|
||||
RootedPropertyName label(context, tokenStream.currentName());
|
||||
RootedPropertyName label(context, labelIdentifier(yieldHandling));
|
||||
if (!label)
|
||||
return null();
|
||||
|
||||
auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
|
||||
return stmt->label() == label;
|
||||
@ -5887,6 +5894,8 @@ Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
|
||||
return null();
|
||||
}
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_COLON);
|
||||
|
||||
/* Push a label struct and parse the statement. */
|
||||
@ -6021,19 +6030,12 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
||||
if (!catchName)
|
||||
return null();
|
||||
break;
|
||||
case TOK_YIELD:
|
||||
if (yieldHandling == YieldIsKeyword) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return null();
|
||||
}
|
||||
|
||||
// Even if yield is *not* necessarily a keyword, we still must
|
||||
// check its validity for legacy generators.
|
||||
if (!checkYieldNameValidity())
|
||||
case TOK_NAME:
|
||||
case TOK_YIELD: {
|
||||
RootedPropertyName param(context, bindingIdentifier(yieldHandling));
|
||||
if (!param)
|
||||
return null();
|
||||
MOZ_FALLTHROUGH;
|
||||
case TOK_NAME: {
|
||||
RootedPropertyName param(context, tokenStream.currentName());
|
||||
catchName = newName(param);
|
||||
if (!catchName)
|
||||
return null();
|
||||
@ -6233,13 +6235,10 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
return null();
|
||||
|
||||
RootedPropertyName name(context);
|
||||
if (tt == TOK_NAME) {
|
||||
name = tokenStream.currentName();
|
||||
} else if (tt == TOK_YIELD) {
|
||||
if (!checkYieldNameValidity())
|
||||
if (tt == TOK_NAME || tt == TOK_YIELD) {
|
||||
name = bindingIdentifier(yieldHandling);
|
||||
if (!name)
|
||||
return null();
|
||||
MOZ_ASSERT(yieldHandling != YieldIsKeyword);
|
||||
name = tokenStream.currentName();
|
||||
} else if (classContext == ClassStatement) {
|
||||
if (defaultHandling == AllowDefaultName) {
|
||||
name = context->names().starDefaultStar;
|
||||
@ -6308,6 +6307,13 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
|
||||
bool isStatic = false;
|
||||
if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) {
|
||||
MOZ_ASSERT(pc->sc()->strict(), "classes are always strict");
|
||||
|
||||
// Strict mode forbids "class" as Identifier, so it can only be the
|
||||
// unescaped keyword.
|
||||
if (!checkUnescapedName())
|
||||
return null();
|
||||
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
|
||||
return null();
|
||||
if (tt == TOK_RC) {
|
||||
@ -6438,10 +6444,9 @@ template <class ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME),
|
||||
"TOK_LET should have been summarily considered a "
|
||||
"LexicalDeclaration");
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
|
||||
MOZ_ASSERT(tokenStream.currentName() == context->names().let);
|
||||
MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
|
||||
|
||||
#ifdef DEBUG
|
||||
TokenKind verify;
|
||||
@ -6455,6 +6460,9 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
|
||||
|
||||
// Otherwise a let declaration must have a name.
|
||||
if (next == TOK_NAME) {
|
||||
MOZ_ASSERT(tokenStream.nextName() != context->names().yield,
|
||||
"token stream should interpret 'yield' as TOK_YIELD");
|
||||
|
||||
// One non-"yield" TOK_NAME edge case deserves special comment.
|
||||
// Consider this:
|
||||
//
|
||||
@ -6466,22 +6474,21 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
|
||||
// that we should parse this as two ExpressionStatements? No. ASI
|
||||
// resolves during parsing. Static semantics only apply to the full
|
||||
// parse tree with ASI applied. No backsies!
|
||||
if (tokenStream.nextName() != context->names().yield)
|
||||
return true;
|
||||
} else if (next != TOK_YIELD) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We have the name "yield": the grammar parameter exactly states whether
|
||||
// this is okay. Even if YieldIsKeyword, the code might be valid if ASI
|
||||
// induces a preceding semicolon. If YieldIsName, the code is valid
|
||||
// outside strict mode, and declaration-parsing code will enforce strict
|
||||
// mode restrictions.
|
||||
//
|
||||
// No checkYieldNameValidity for TOK_YIELD is needed here. It'll happen
|
||||
// when TOK_YIELD is consumed as BindingIdentifier or as start of a fresh
|
||||
// Statement.
|
||||
return yieldHandling == YieldIsName;
|
||||
// If we have the name "yield", the grammar parameter exactly states
|
||||
// whether this is okay. (This wasn't true for SpiderMonkey's ancient
|
||||
// legacy generator syntax, but that's dead now.) If YieldIsName,
|
||||
// declaration-parsing code will (if necessary) enforce a strict mode
|
||||
// restriction on defining "yield". If YieldIsKeyword, consider this the
|
||||
// end of the declaration, in case ASI induces a semicolon that makes the
|
||||
// "yield" valid.
|
||||
if (next == TOK_YIELD)
|
||||
return yieldHandling == YieldIsName;
|
||||
|
||||
// Otherwise not a let declaration.
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
@ -6535,11 +6542,10 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next, modifier))
|
||||
return null();
|
||||
if (next == TOK_COLON) {
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
|
||||
if (next == TOK_COLON)
|
||||
return labeledStatement(yieldHandling);
|
||||
}
|
||||
|
||||
return expressionStatement(yieldHandling);
|
||||
}
|
||||
|
||||
@ -6548,25 +6554,21 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
MOZ_ASSERT(!pc->sc()->strict(),
|
||||
"observing |let| as TOK_NAME and not TOK_LET implies "
|
||||
"non-strict code (and the edge case of 'use strict' "
|
||||
"immediately followed by |let| on a new line only "
|
||||
"applies to StatementListItems, not to Statements)");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Statement context forbids LexicalDeclaration.
|
||||
if ((next == TOK_LB || next == TOK_LC || next == TOK_NAME) &&
|
||||
// |let| here can only be an Identifier, not a declaration. Give nicer
|
||||
// errors for declaration-looking typos.
|
||||
if (!tokenStream.currentToken().nameContainsEscape() &&
|
||||
tokenStream.currentName() == context->names().let)
|
||||
{
|
||||
bool forbiddenLetDeclaration = false;
|
||||
if (next == TOK_LB) {
|
||||
// ExpressionStatement has a 'let [' lookahead restriction.
|
||||
|
||||
if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) {
|
||||
// |let| can't be an Identifier in strict mode code. Ditto for
|
||||
// non-standard JavaScript 1.7+.
|
||||
forbiddenLetDeclaration = true;
|
||||
} else {
|
||||
} else if (next == TOK_LB) {
|
||||
// Enforce ExpressionStatement's 'let [' lookahead restriction.
|
||||
forbiddenLetDeclaration = true;
|
||||
} else if (next == TOK_LC || next == TOK_NAME) {
|
||||
// 'let {' and 'let foo' aren't completely forbidden, if ASI
|
||||
// causes 'let' to be the entire Statement. But if they're
|
||||
// same-line, we can aggressively give a better error message.
|
||||
@ -6694,14 +6696,6 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
|
||||
report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
|
||||
return null();
|
||||
|
||||
// TOK_LET implies we're in strict mode code where static semantics
|
||||
// forbid IdentifierName to be "let": a stronger restriction than
|
||||
// Statement's lookahead restriction on |let [|. Provide a better error
|
||||
// message here than the default case would.
|
||||
case TOK_LET:
|
||||
report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "let declarations");
|
||||
return null();
|
||||
|
||||
// NOTE: default case handled in the ExpressionStatement section.
|
||||
}
|
||||
}
|
||||
@ -6757,11 +6751,10 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next, modifier))
|
||||
return null();
|
||||
if (next == TOK_COLON) {
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
|
||||
if (next == TOK_COLON)
|
||||
return labeledStatement(yieldHandling);
|
||||
}
|
||||
|
||||
return expressionStatement(yieldHandling);
|
||||
}
|
||||
|
||||
@ -6770,23 +6763,11 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
if (nextTokenContinuesLetDeclaration(next, yieldHandling))
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
|
||||
// IdentifierName can't be "let" in strict mode code. |let| in
|
||||
// strict mode code is usually TOK_LET, but in this one weird case
|
||||
// in global code it's TOK_NAME:
|
||||
//
|
||||
// "use strict" // ExpressionStatement ended by ASI
|
||||
// let <...whatever else...> // a fresh StatementListItem
|
||||
//
|
||||
// Carefully reject strict mode |let| non-declarations.
|
||||
if (pc->sc()->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
|
||||
"declaration pattern", TokenKindToDesc(next));
|
||||
return null();
|
||||
}
|
||||
if (!tokenStream.currentToken().nameContainsEscape() &&
|
||||
tokenStream.currentName() == context->names().let &&
|
||||
nextTokenContinuesLetDeclaration(next, yieldHandling))
|
||||
{
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
}
|
||||
|
||||
if (next == TOK_COLON)
|
||||
@ -6872,11 +6853,10 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
|
||||
|
||||
// LexicalDeclaration[In, ?Yield]
|
||||
// LetOrConst BindingList[?In, ?Yield]
|
||||
case TOK_LET:
|
||||
case TOK_CONST:
|
||||
// [In] is the default behavior, because for-loops specially parse
|
||||
// their heads to handle |in| in this situation.
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ tt == TOK_CONST);
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ true);
|
||||
|
||||
// ImportDeclaration (only inside modules)
|
||||
case TOK_IMPORT:
|
||||
@ -7248,8 +7228,13 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
|
||||
if (tt == TOK_NAME) {
|
||||
if (!tokenStream.nextTokenEndsExpr(&endsExpr))
|
||||
return null();
|
||||
if (endsExpr)
|
||||
return identifierName(yieldHandling);
|
||||
if (endsExpr) {
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
return identifierReference(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (tt == TOK_NUMBER) {
|
||||
@ -8258,18 +8243,99 @@ Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::identifierName(YieldHandling yieldHandling)
|
||||
PropertyName*
|
||||
Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling)
|
||||
{
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
if (yieldHandling == YieldIsKeyword && name == context->names().yield) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return null();
|
||||
PropertyName* ident;
|
||||
const Token& tok = tokenStream.currentToken();
|
||||
if (tok.type == TOK_NAME) {
|
||||
ident = tok.name();
|
||||
MOZ_ASSERT(ident != context->names().yield,
|
||||
"tokenizer should have treated 'yield' as TOK_YIELD");
|
||||
|
||||
if (pc->sc()->strict()) {
|
||||
const char* badName = ident == context->names().let
|
||||
? "let"
|
||||
: ident == context->names().static_
|
||||
? "static"
|
||||
: nullptr;
|
||||
if (badName) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(tok.type == TOK_YIELD);
|
||||
|
||||
if (yieldHandling == YieldIsKeyword ||
|
||||
pc->sc()->strict() ||
|
||||
pc->isStarGenerator() ||
|
||||
versionNumber() >= JSVERSION_1_7)
|
||||
{
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ident = context->names().yield;
|
||||
}
|
||||
|
||||
// If we're inside a function that later becomes a legacy generator, then
|
||||
// a |yield| identifier name here will be detected by a subsequent
|
||||
// |checkYieldNameValidity| call.
|
||||
return ident;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
PropertyName*
|
||||
Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
|
||||
{
|
||||
PropertyName* ident;
|
||||
const Token& tok = tokenStream.currentToken();
|
||||
if (tok.type == TOK_NAME) {
|
||||
ident = tok.name();
|
||||
MOZ_ASSERT(ident != context->names().yield,
|
||||
"tokenizer should have treated 'yield' as TOK_YIELD");
|
||||
|
||||
if (pc->sc()->strict()) {
|
||||
const char* badName = ident == context->names().arguments
|
||||
? "arguments"
|
||||
: ident == context->names().eval
|
||||
? "eval"
|
||||
: nullptr;
|
||||
if (badName) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
badName = ident == context->names().let
|
||||
? "let"
|
||||
: ident == context->names().static_
|
||||
? "static"
|
||||
: nullptr;
|
||||
if (badName) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(tok.type == TOK_YIELD);
|
||||
|
||||
if (yieldHandling == YieldIsKeyword ||
|
||||
pc->sc()->strict() ||
|
||||
pc->isStarGenerator() ||
|
||||
versionNumber() >= JSVERSION_1_7)
|
||||
{
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ident = context->names().yield;
|
||||
}
|
||||
|
||||
return ident;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::identifierReference(Handle<PropertyName*> name)
|
||||
{
|
||||
Node pn = newName(name);
|
||||
if (!pn)
|
||||
return null();
|
||||
@ -8670,7 +8736,11 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
|
||||
if (!tokenStream.checkForKeyword(propAtom, nullptr))
|
||||
return null();
|
||||
|
||||
Node nameExpr = identifierName(yieldHandling);
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
Node nameExpr = identifierReference(name);
|
||||
if (!nameExpr)
|
||||
return null();
|
||||
|
||||
@ -8684,7 +8754,11 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
|
||||
if (!tokenStream.checkForKeyword(propAtom, nullptr))
|
||||
return null();
|
||||
|
||||
Node lhs = identifierName(yieldHandling);
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
Node lhs = identifierReference(name);
|
||||
if (!lhs)
|
||||
return null();
|
||||
|
||||
@ -8693,7 +8767,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
|
||||
Node rhs;
|
||||
{
|
||||
// Clearing `inDestructuringDecl` allows name use to be noted
|
||||
// in `identifierName`. See bug 1255167.
|
||||
// in Parser::identifierReference. See bug 1255167.
|
||||
AutoClearInDestructuringDecl autoClear(pc);
|
||||
rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!rhs)
|
||||
@ -8900,11 +8974,13 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
return stringLiteral();
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity())
|
||||
case TOK_NAME: {
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
MOZ_FALLTHROUGH;
|
||||
case TOK_NAME:
|
||||
return identifierName(yieldHandling);
|
||||
|
||||
return identifierReference(name);
|
||||
}
|
||||
|
||||
case TOK_REGEXP:
|
||||
return newRegExp();
|
||||
@ -8946,12 +9022,11 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
TokenKind next;
|
||||
if (!tokenStream.getToken(&next))
|
||||
return null();
|
||||
// FIXME: This fails to handle a rest parameter named |yield| correctly
|
||||
// outside of generators: |var f = (...yield) => 42;| should be
|
||||
// valid code! When this is fixed, make sure to consult both
|
||||
// |yieldHandling| and |checkYieldNameValidity| for correctness
|
||||
// until legacy generator syntax is removed.
|
||||
if (next != TOK_NAME) {
|
||||
|
||||
// This doesn't check that the provided name is allowed, e.g. if the
|
||||
// enclosing code is strict mode code, any of "arguments", "let", or
|
||||
// "yield" should be prohibited. Argument-parsing code handles that.
|
||||
if (next != TOK_NAME && next != TOK_YIELD) {
|
||||
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
|
||||
"rest argument name", TokenKindToDesc(next));
|
||||
return null();
|
||||
|
@ -981,11 +981,15 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
|
||||
// Determine whether |yield| is a valid name in the current context, or
|
||||
// whether it's prohibited due to strictness, JS version, or occurrence
|
||||
// inside a star generator.
|
||||
bool checkYieldNameValidity();
|
||||
bool yieldExpressionsSupported() {
|
||||
return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
|
||||
}
|
||||
|
||||
// Match the current token against the BindingIdentifier production with
|
||||
// the given Yield parameter. If there is no match, report a syntax
|
||||
// error.
|
||||
PropertyName* bindingIdentifier(YieldHandling yieldHandling);
|
||||
|
||||
virtual bool strictMode() { return pc->sc()->strict(); }
|
||||
bool setLocalStrictMode(bool strict) {
|
||||
MOZ_ASSERT(tokenStream.debugHasNoLookahead());
|
||||
@ -1206,7 +1210,21 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
|
||||
DefaultHandling defaultHandling);
|
||||
|
||||
Node identifierName(YieldHandling yieldHandling);
|
||||
PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
|
||||
|
||||
PropertyName* labelIdentifier(YieldHandling yieldHandling) {
|
||||
return labelOrIdentifierReference(yieldHandling);
|
||||
}
|
||||
|
||||
PropertyName* identifierReference(YieldHandling yieldHandling) {
|
||||
return labelOrIdentifierReference(yieldHandling);
|
||||
}
|
||||
|
||||
PropertyName* importedBinding() {
|
||||
return bindingIdentifier(YieldIsName);
|
||||
}
|
||||
|
||||
Node identifierReference(Handle<PropertyName*> name);
|
||||
|
||||
bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);
|
||||
|
||||
|
@ -107,7 +107,6 @@
|
||||
macro(THROW, "keyword 'throw'") \
|
||||
macro(DEBUGGER, "keyword 'debugger'") \
|
||||
macro(YIELD, "keyword 'yield'") \
|
||||
macro(LET, "keyword 'let'") \
|
||||
macro(EXPORT, "keyword 'export'") \
|
||||
macro(IMPORT, "keyword 'import'") \
|
||||
macro(CLASS, "keyword 'class'") \
|
||||
@ -239,12 +238,6 @@ TokenKindIsAssignment(TokenKind tt)
|
||||
return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
|
||||
}
|
||||
|
||||
inline bool
|
||||
TokenKindIsDecl(TokenKind tt)
|
||||
{
|
||||
return tt == TOK_VAR || tt == TOK_LET;
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
@ -981,11 +981,6 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
||||
if (kw->tokentype == TOK_STRICT_RESERVED)
|
||||
return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
|
||||
|
||||
// Treat 'let' as an identifier and contextually a keyword in sloppy mode.
|
||||
// It is always a keyword in strict mode.
|
||||
if (kw->tokentype == TOK_LET && !strictMode())
|
||||
return true;
|
||||
|
||||
// Working keyword.
|
||||
if (ttp) {
|
||||
*ttp = kw->tokentype;
|
||||
|
@ -351,6 +351,13 @@ class MOZ_STACK_CLASS TokenStream
|
||||
return nextToken().name();
|
||||
}
|
||||
|
||||
bool nextNameContainsEscape() const {
|
||||
if (nextToken().type == TOK_YIELD)
|
||||
return false;
|
||||
MOZ_ASSERT(nextToken().type == TOK_NAME);
|
||||
return nextToken().nameContainsEscape();
|
||||
}
|
||||
|
||||
bool isCurrentTokenAssignment() const {
|
||||
return TokenKindIsAssignment(currentToken().type);
|
||||
}
|
||||
|
@ -2529,11 +2529,14 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
|
||||
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<ArgumentsObject>()) {
|
||||
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<ProxyObject>()) {
|
||||
tenuredSize += ProxyObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp()) {
|
||||
op(dst, src);
|
||||
} else if (src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE) {
|
||||
// Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
|
||||
// to ensure any additional nursery buffers they hold are moved.
|
||||
} else if (src->getClass()->hasFinalize()) {
|
||||
// Such objects need to be handled specially above to ensure any
|
||||
// additional nursery buffers they hold are moved.
|
||||
MOZ_RELEASE_ASSERT(CanNurseryAllocateFinalizedClass(src->getClass()));
|
||||
MOZ_CRASH("Unhandled JSCLASS_SKIP_NURSERY_FINALIZE Class");
|
||||
}
|
||||
|
||||
@ -2804,30 +2807,6 @@ FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTION
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
|
||||
/*** Type Marking *********************************************************************************/
|
||||
|
||||
void
|
||||
TypeSet::MarkTypeRoot(JSTracer* trc, TypeSet::Type* v, const char* name)
|
||||
{
|
||||
AssertRootMarkingPhase(trc);
|
||||
MarkTypeUnbarriered(trc, v, name);
|
||||
}
|
||||
|
||||
void
|
||||
TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name)
|
||||
{
|
||||
if (v->isSingletonUnchecked()) {
|
||||
JSObject* obj = v->singletonNoBarrier();
|
||||
DispatchToTracer(trc, &obj, name);
|
||||
*v = TypeSet::ObjectType(obj);
|
||||
} else if (v->isGroupUnchecked()) {
|
||||
ObjectGroup* group = v->groupNoBarrier();
|
||||
DispatchToTracer(trc, &group, name);
|
||||
*v = TypeSet::ObjectType(group);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*** Cycle Collector Barrier Implementation *******************************************************/
|
||||
|
||||
|
@ -254,13 +254,9 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
|
||||
/* Ensure there's enough space to replace the contents with a RelocationOverlay. */
|
||||
MOZ_ASSERT(size >= sizeof(RelocationOverlay));
|
||||
|
||||
/*
|
||||
* Classes with JSCLASS_SKIP_NURSERY_FINALIZE will not have their finalizer
|
||||
* called if they are nursery allocated and not promoted to the tenured
|
||||
* heap. The finalizers for these classes must do nothing except free data
|
||||
* which was allocated via Nursery::allocateBuffer.
|
||||
*/
|
||||
MOZ_ASSERT_IF(clasp->hasFinalize(), clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
|
||||
/* Sanity check the finalizer. */
|
||||
MOZ_ASSERT_IF(clasp->hasFinalize(), CanNurseryAllocateFinalizedClass(clasp) ||
|
||||
clasp->isProxy());
|
||||
|
||||
/* Make the object allocation. */
|
||||
JSObject* obj = static_cast<JSObject*>(allocate(size));
|
||||
@ -270,7 +266,7 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
|
||||
/* If we want external slots, add them. */
|
||||
HeapSlot* slots = nullptr;
|
||||
if (numDynamic) {
|
||||
MOZ_ASSERT(clasp->isNative());
|
||||
MOZ_ASSERT(clasp->isNative() || clasp->isProxy());
|
||||
slots = static_cast<HeapSlot*>(allocateBuffer(cx->zone(), numDynamic * sizeof(HeapSlot)));
|
||||
if (!slots) {
|
||||
/*
|
||||
@ -700,7 +696,7 @@ js::Nursery::doCollection(JSRuntime* rt, JS::gcreason::Reason reason,
|
||||
// Sweep compartments to update the array buffer object's view lists.
|
||||
maybeStartProfile(ProfileKey::SweepArrayBufferViewList);
|
||||
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
|
||||
c->sweepAfterMinorGC();
|
||||
c->sweepAfterMinorGC(&mover);
|
||||
maybeEndProfile(ProfileKey::SweepArrayBufferViewList);
|
||||
|
||||
// Update any slot or element pointers whose destination has been tenured.
|
||||
|
@ -112,6 +112,20 @@ class TenuringTracer : public JSTracer
|
||||
void traceSlots(JS::Value* vp, JS::Value* end);
|
||||
};
|
||||
|
||||
/*
|
||||
* Classes with JSCLASS_SKIP_NURSERY_FINALIZE or Wrapper classes with
|
||||
* CROSS_COMPARTMENT flags will not have their finalizer called if they are
|
||||
* nursery allocated and not promoted to the tenured heap. The finalizers for
|
||||
* these classes must do nothing except free data which was allocated via
|
||||
* Nursery::allocateBuffer.
|
||||
*/
|
||||
inline bool
|
||||
CanNurseryAllocateFinalizedClass(const js::Class* const clasp)
|
||||
{
|
||||
MOZ_ASSERT(clasp->hasFinalize());
|
||||
return clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE;
|
||||
}
|
||||
|
||||
class Nursery
|
||||
{
|
||||
public:
|
||||
|
178
js/src/gc/NurseryAwareHashMap.h
Normal file
178
js/src/gc/NurseryAwareHashMap.h
Normal file
@ -0,0 +1,178 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
#ifndef gc_NurseryAwareHashMap_h
|
||||
#define gc_NurseryAwareHashMap_h
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace detail {
|
||||
// This class only handles the incremental case and does not deal with nursery
|
||||
// pointers. The only users should be for NurseryAwareHashMap; it is defined
|
||||
// externally because we need a GCPolicy for its use in the contained map.
|
||||
template <typename T>
|
||||
class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
|
||||
{
|
||||
public:
|
||||
UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
||||
MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
|
||||
explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
|
||||
UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
|
||||
: ReadBarrieredBase<T>(mozilla::Forward<UnsafeBareReadBarriered<T>>(v))
|
||||
{}
|
||||
|
||||
UnsafeBareReadBarriered& operator=(const UnsafeBareReadBarriered& v) {
|
||||
this->value = v.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
UnsafeBareReadBarriered& operator=(const T& v) {
|
||||
this->value = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T get() const {
|
||||
if (!InternalBarrierMethods<T>::isMarkable(this->value))
|
||||
return JS::GCPolicy<T>::initial();
|
||||
this->read();
|
||||
return this->value;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return bool(this->value);
|
||||
}
|
||||
|
||||
const T unbarrieredGet() const { return this->value; }
|
||||
T* unsafeGet() { return &this->value; }
|
||||
T const* unsafeGet() const { return &this->value; }
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// The "nursery aware" hash map is a special case of GCHashMap that is able to
|
||||
// treat nursery allocated members weakly during a minor GC: e.g. it allows for
|
||||
// nursery allocated objects to be collected during nursery GC where a normal
|
||||
// hash table treats such edges strongly.
|
||||
//
|
||||
// Doing this requires some strong constraints on what can be stored in this
|
||||
// table and how it can be accessed. At the moment, this table assumes that
|
||||
// all values contain a strong reference to the key. It also requires the
|
||||
// policy to contain an |isTenured| and |needsSweep| members, which is fairly
|
||||
// non-standard. This limits its usefulness to the CrossCompartmentMap at the
|
||||
// moment, but might serve as a useful base for other tables in future.
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename HashPolicy = DefaultHasher<Key>,
|
||||
typename AllocPolicy = TempAllocPolicy>
|
||||
class NurseryAwareHashMap
|
||||
{
|
||||
using BarrieredValue = detail::UnsafeBareReadBarriered<Value>;
|
||||
using MapType = GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>;
|
||||
MapType map;
|
||||
|
||||
// Keep a list of all keys for which JS::GCPolicy<Key>::isTenured is false.
|
||||
// This lets us avoid a full traveral of the map on each minor GC, keeping
|
||||
// the minor GC times proportional to the nursery heap size.
|
||||
Vector<Key, 0, AllocPolicy> nurseryEntries;
|
||||
|
||||
public:
|
||||
using Lookup = typename MapType::Lookup;
|
||||
using Ptr = typename MapType::Ptr;
|
||||
using Range = typename MapType::Range;
|
||||
|
||||
explicit NurseryAwareHashMap(AllocPolicy a = AllocPolicy()) : map(a) {}
|
||||
|
||||
MOZ_MUST_USE bool init(uint32_t len = 16) { return map.init(len); }
|
||||
|
||||
bool empty() const { return map.empty(); }
|
||||
Ptr lookup(const Lookup& l) const { return map.lookup(l); }
|
||||
void remove(Ptr p) { map.remove(p); }
|
||||
Range all() const { return map.all(); }
|
||||
struct Enum : public MapType::Enum {
|
||||
explicit Enum(NurseryAwareHashMap& namap) : MapType::Enum(namap.map) {}
|
||||
};
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return map.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return map.sizeOfIncludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool put(const Key& k, const Value& v) {
|
||||
auto p = map.lookupForAdd(k);
|
||||
if (p) {
|
||||
if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
|
||||
if (!nurseryEntries.append(k))
|
||||
return false;
|
||||
}
|
||||
p->value() = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok = map.add(p, k, v);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
|
||||
if (!nurseryEntries.append(k)) {
|
||||
map.remove(k);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sweepAfterMinorGC(JSTracer* trc) {
|
||||
for (auto& key : nurseryEntries) {
|
||||
auto p = map.lookup(key);
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
// Drop the entry if the value is not marked.
|
||||
if (JS::GCPolicy<BarrieredValue>::needsSweep(&p->value())) {
|
||||
map.remove(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update and relocate the key, if the value is still needed.
|
||||
//
|
||||
// Note that this currently assumes that all Value will contain a
|
||||
// strong reference to Key, as per its use as the
|
||||
// CrossCompartmentWrapperMap. We may need to make the following
|
||||
// behavior more dynamic if we use this map in other nursery-aware
|
||||
// contexts.
|
||||
Key copy(key);
|
||||
mozilla::DebugOnly<bool> sweepKey = JS::GCPolicy<Key>::needsSweep(©);
|
||||
MOZ_ASSERT(!sweepKey);
|
||||
map.rekeyIfMoved(key, copy);
|
||||
}
|
||||
nurseryEntries.clear();
|
||||
}
|
||||
|
||||
void sweep() {
|
||||
MOZ_ASSERT(nurseryEntries.empty());
|
||||
map.sweep();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
template <typename T>
|
||||
struct GCPolicy<js::detail::UnsafeBareReadBarriered<T>>
|
||||
{
|
||||
static void trace(JSTracer* trc, js::detail::UnsafeBareReadBarriered<T>* thingp,
|
||||
const char* name)
|
||||
{
|
||||
js::TraceEdge(trc, thingp, name);
|
||||
}
|
||||
static bool needsSweep(js::detail::UnsafeBareReadBarriered<T>* thingp) {
|
||||
return js::gc::IsAboutToBeFinalized(thingp);
|
||||
}
|
||||
};
|
||||
} // namespace JS
|
||||
|
||||
#endif // gc_NurseryAwareHashMap_h
|
@ -153,37 +153,102 @@ assertEq(evaled, true);
|
||||
|
||||
assertThrowsInstanceOf(() => new Function('var [...rest = defaults] = [];'), SyntaxError);
|
||||
|
||||
// bug 1124480: destructuring defaults should work correctly as part of for-in
|
||||
var defaultsSupportedInForVar = false;
|
||||
try
|
||||
{
|
||||
new Function('for (var [a, b = 10] in " ") {}');
|
||||
defaultsSupportedInForVar = true;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
}
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (var [a, b = 10] in " ") {}
|
||||
assertEq(b, 10);`)();
|
||||
|
||||
if (defaultsSupportedInForVar) {
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (var [a, b = 10] in " ") {}
|
||||
assertEq(b, 10);
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (let [a, c = 10] in " ") { b = c; }
|
||||
assertEq(b, 10);`)();
|
||||
|
||||
b = undefined;
|
||||
for (let [a, c = 10] in " ") { b = c; }
|
||||
assertEq(b, 10);
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (let [a, c = (x => y)] in " ") { b = c; }
|
||||
assertEq(typeof b, "function");`)();
|
||||
|
||||
b = undefined;
|
||||
for (var {1: b = 10} in " ") {}
|
||||
assertEq(b, 10);
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (let [a, __proto__ = 10] in " ") { b = __proto__; }
|
||||
assertEq(b, 10);`)();
|
||||
|
||||
b = undefined;
|
||||
for (let {1: c = 10} in " ") { b = c; }
|
||||
assertEq(b, 10);
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (let [a, __proto__ = (x => y)] in " ") { b = __proto__; }
|
||||
assertEq(typeof b, "function");`)();
|
||||
|
||||
b = undefined;
|
||||
for (let {c = 10} in " ") { b = c; }
|
||||
assertEq(b, 10);
|
||||
`)();
|
||||
}
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (var {1: b = 10} in " ") {}
|
||||
assertEq(b, 10);`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (let {1: c = 10} in " ") { b = c; }
|
||||
assertEq(b, 10);`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (let { c = 10 } in " ") { b = c; }
|
||||
assertEq(b, 10);`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
assertEq(Number.prototype.a, undefined);
|
||||
for (var { a: c = (x => y) } in [{}]) { b = c; }
|
||||
assertEq(typeof b, "function");`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
Object.defineProperty(String.prototype, "__proto__",
|
||||
{ value: undefined, configurable: true });
|
||||
for (var { __proto__: c = (x => y) } in [{}]) { b = c; }
|
||||
delete String.prototype.__proto__;
|
||||
assertEq(typeof b, "function");`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (var { a: c = (x => y) } of [{ a: undefined }]) { b = c; }
|
||||
assertEq(typeof b, "function");`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
for (var { __proto__: c = (x => y) } of [{ ["__proto__"]: undefined }]) { b = c; }
|
||||
assertEq(typeof b, "function");`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
var ts = Function.prototype.toString;
|
||||
Function.prototype.toString = () => 'hi';
|
||||
String.prototype.hi = 42;
|
||||
for (var { [(x => y)]: c } in [0]) { b = c; }
|
||||
Function.prototype.toString = ts;
|
||||
delete String.prototype.hi;
|
||||
assertEq(b, 42);`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
var ts = Function.prototype.toString;
|
||||
Function.prototype.toString = () => 'hi';
|
||||
String.prototype.hi = 42;
|
||||
for (var { [(x => y)]: __proto__ } in [0]) { b = __proto__; }
|
||||
Function.prototype.toString = ts;
|
||||
delete String.prototype.hi;
|
||||
assertEq(b, 42);`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
var ts = Function.prototype.toString;
|
||||
Function.prototype.toString = () => 'hi';
|
||||
for (var { [(x => y)]: c } of [{ 'hi': 42 }]) { b = c; }
|
||||
Function.prototype.toString = ts;
|
||||
assertEq(b, 42);`)();
|
||||
|
||||
new Function(`
|
||||
b = undefined;
|
||||
var ts = Function.prototype.toString;
|
||||
Function.prototype.toString = () => 'hi';
|
||||
for (var { [(x => y)]: __proto__ } of [{ hi: 42 }]) { b = __proto__; }
|
||||
Function.prototype.toString = ts;
|
||||
assertEq(b, 42);`)();
|
||||
|
2
js/src/jit-test/tests/promise/bug-1298776.js
Normal file
2
js/src/jit-test/tests/promise/bug-1298776.js
Normal file
@ -0,0 +1,2 @@
|
||||
if (typeof oomTest === 'function')
|
||||
oomTest(Function(`new Promise(res=>res)`));
|
21
js/src/jit-test/tests/regexp/clone-statics.js
Normal file
21
js/src/jit-test/tests/regexp/clone-statics.js
Normal file
@ -0,0 +1,21 @@
|
||||
if (helperThreadCount() === 0)
|
||||
quit(0);
|
||||
|
||||
offThreadCompileScript(`
|
||||
function foo(x, {}) {
|
||||
do {
|
||||
re = /erwe/;
|
||||
if (x === 1)
|
||||
re.x = 1;
|
||||
else
|
||||
re.x = "a";
|
||||
assertEq(re.x.length, (x === 1) ? undefined : 1);
|
||||
} while (!inIon());
|
||||
}
|
||||
|
||||
foo(0, 0);
|
||||
RegExp.multiline = 1;
|
||||
foo(1, 0);
|
||||
`);
|
||||
|
||||
runOffThreadScript();
|
@ -367,28 +367,28 @@ var {v2i, i2i, i2v} = wasmEvalText(`(module
|
||||
(export "i2v" 8)
|
||||
)`);
|
||||
|
||||
const badIndirectCall = /bad wasm indirect call/;
|
||||
const signatureMismatch = /indirect call signature mismatch/;
|
||||
|
||||
assertEq(v2i(0), 13);
|
||||
assertEq(v2i(1), 42);
|
||||
assertErrorMessage(() => v2i(2), Error, badIndirectCall);
|
||||
assertErrorMessage(() => v2i(3), Error, badIndirectCall);
|
||||
assertErrorMessage(() => v2i(4), Error, badIndirectCall);
|
||||
assertErrorMessage(() => v2i(5), Error, badIndirectCall);
|
||||
assertErrorMessage(() => v2i(2), Error, signatureMismatch);
|
||||
assertErrorMessage(() => v2i(3), Error, signatureMismatch);
|
||||
assertErrorMessage(() => v2i(4), Error, signatureMismatch);
|
||||
assertErrorMessage(() => v2i(5), Error, signatureMismatch);
|
||||
|
||||
assertErrorMessage(() => i2i(0), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2i(1), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2i(0), Error, signatureMismatch);
|
||||
assertErrorMessage(() => i2i(1), Error, signatureMismatch);
|
||||
assertEq(i2i(2, 100), 101);
|
||||
assertEq(i2i(3, 100), 102);
|
||||
assertEq(i2i(4, 100), 103);
|
||||
assertEq(i2i(5, 100), 104);
|
||||
|
||||
assertErrorMessage(() => i2v(0), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2v(1), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2v(2), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2v(3), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2v(4), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2v(5), Error, badIndirectCall);
|
||||
assertErrorMessage(() => i2v(0), Error, signatureMismatch);
|
||||
assertErrorMessage(() => i2v(1), Error, signatureMismatch);
|
||||
assertErrorMessage(() => i2v(2), Error, signatureMismatch);
|
||||
assertErrorMessage(() => i2v(3), Error, signatureMismatch);
|
||||
assertErrorMessage(() => i2v(4), Error, signatureMismatch);
|
||||
assertErrorMessage(() => i2v(5), Error, signatureMismatch);
|
||||
|
||||
{
|
||||
enableSPSProfiling();
|
||||
|
@ -22,7 +22,7 @@ var e = i.exports;
|
||||
var t = e.tbl;
|
||||
var f = t.get(0);
|
||||
assertEq(f(), e.call(0));
|
||||
assertErrorMessage(() => e.call(1), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => e.call(1), Error, /indirect call to null/);
|
||||
assertErrorMessage(() => e.call(2), Error, /out-of-range/);
|
||||
assertEq(finalizeCount(), 0);
|
||||
i.edge = makeFinalizeObserver();
|
||||
@ -79,8 +79,7 @@ t = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 4);
|
||||
|
||||
// The bad-indirect-call stub should (currently, could be changed later) keep
|
||||
// the instance containing that stub alive.
|
||||
// Null elements shouldn't keep anything alive.
|
||||
resetFinalizeCount();
|
||||
var i = evalText(`(module (table (resizable 2)) (export "tbl" table) ${caller})`);
|
||||
var e = i.exports;
|
||||
@ -96,7 +95,7 @@ gc();
|
||||
assertEq(finalizeCount(), 1);
|
||||
i = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 1);
|
||||
assertEq(finalizeCount(), 2);
|
||||
t = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 3);
|
||||
@ -144,14 +143,13 @@ assertEq(finalizeCount(), 2);
|
||||
t.set(0, null);
|
||||
assertEq(t.get(0), null);
|
||||
gc();
|
||||
assertEq(finalizeCount(), 2);
|
||||
assertEq(finalizeCount(), 3);
|
||||
t = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 4);
|
||||
|
||||
// Once all of an instance's elements in a Table (including the
|
||||
// bad-indirect-call stub) have been clobbered, the Instance should not be
|
||||
// rooted.
|
||||
// Once all of an instance's elements in a Table have been clobbered, the
|
||||
// Instance should not be reachable.
|
||||
resetFinalizeCount();
|
||||
var i1 = evalText(`(module (func $f1 (result i32) (i32.const 13)) (export "f1" $f1))`);
|
||||
var i2 = evalText(`(module (func $f2 (result i32) (i32.const 42)) (export "f2" $f2))`);
|
||||
|
24
js/src/jit-test/tests/wasm/table-pre-barrier.js
Normal file
24
js/src/jit-test/tests/wasm/table-pre-barrier.js
Normal file
@ -0,0 +1,24 @@
|
||||
load(libdir + "wasm.js");
|
||||
|
||||
const Module = WebAssembly.Module;
|
||||
const Instance = WebAssembly.Instance;
|
||||
const Table = WebAssembly.Table;
|
||||
|
||||
var i42 = new Instance(new Module(textToBinary(`(module (func (result i32) (i32.const 42)) (export "f" 0))`)));
|
||||
var i13 = new Instance(new Module(textToBinary(`(module (func (result i32) (i32.const 13)) (export "f" 0))`)));
|
||||
var t = new Table({element:"anyfunc", initial:1});
|
||||
t.set(0, i42.exports.f);
|
||||
|
||||
assertEq(t.get(0)(), 42);
|
||||
|
||||
verifyprebarriers();
|
||||
t.set(0, i13.exports.f);
|
||||
verifyprebarriers();
|
||||
|
||||
assertEq(t.get(0)(), 13);
|
||||
|
||||
verifyprebarriers();
|
||||
t.set(0, null);
|
||||
verifyprebarriers();
|
||||
|
||||
assertEq(t.get(0), null);
|
@ -40,27 +40,27 @@ var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result
|
||||
var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
|
||||
|
||||
var call = evalText(`(module (table (resizable 10)) ${callee(0)} ${caller})`).exports.call;
|
||||
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(0), Error, /indirect call to null/);
|
||||
assertErrorMessage(() => call(10), Error, /out-of-range/);
|
||||
|
||||
var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0)) ${callee(0)} ${caller})`).exports.call;
|
||||
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(0), Error, /indirect call to null/);
|
||||
assertErrorMessage(() => call(10), Error, /out-of-range/);
|
||||
|
||||
var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`).exports.call;
|
||||
assertEq(call(0), 0);
|
||||
assertErrorMessage(() => call(1), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(1), Error, /indirect call to null/);
|
||||
assertErrorMessage(() => call(2), Error, /indirect call to null/);
|
||||
assertErrorMessage(() => call(10), Error, /out-of-range/);
|
||||
|
||||
var call = evalText(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f1) (elem (i32.const 4) $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
|
||||
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(0), Error, /indirect call to null/);
|
||||
assertEq(call(1), 0);
|
||||
assertEq(call(2), 1);
|
||||
assertErrorMessage(() => call(3), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(3), Error, /indirect call to null/);
|
||||
assertEq(call(4), 0);
|
||||
assertEq(call(5), 2);
|
||||
assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(6), Error, /indirect call to null/);
|
||||
assertErrorMessage(() => call(10), Error, /out-of-range/);
|
||||
|
||||
var tbl = new Table({initial:3, element:"anyfunc"});
|
||||
@ -69,7 +69,7 @@ assertEq(call(0), 0);
|
||||
assertEq(call(1), 1);
|
||||
assertEq(tbl.get(0)(), 0);
|
||||
assertEq(tbl.get(1)(), 1);
|
||||
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(2), Error, /indirect call to null/);
|
||||
assertEq(tbl.get(2), null);
|
||||
|
||||
var exp = evalText(`(module (import "a" "b" (table 3)) (export "tbl" table) (elem (i32.const 2) $f2) ${callee(2)} ${caller})`, {a:{b:tbl}}).exports;
|
||||
@ -90,7 +90,7 @@ assertEq(exp1.tbl.get(1), exp1.f0);
|
||||
assertEq(exp1.tbl.get(2), null);
|
||||
assertEq(exp1.call(0), 0);
|
||||
assertEq(exp1.call(1), 0);
|
||||
assertErrorMessage(() => exp1.call(2), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => exp1.call(2), Error, /indirect call to null/);
|
||||
var exp2 = evalText(`(module (import "a" "b" (table 10)) (export "tbl" table) (elem (i32.const 1) $f1 $f1) ${callee(1)} (export "f1" $f1) ${caller})`, {a:{b:exp1.tbl}}).exports
|
||||
assertEq(exp1.tbl, exp2.tbl);
|
||||
assertEq(exp2.tbl.get(0), exp1.f0);
|
||||
@ -112,7 +112,7 @@ tbl.set(1, e2.g);
|
||||
tbl.set(2, e3.h);
|
||||
var e4 = evalText(`(module (import "a" "b" (table 3)) ${caller})`, {a:{b:tbl}}).exports;
|
||||
assertEq(e4.call(0), 42);
|
||||
assertErrorMessage(() => e4.call(1), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => e4.call(1), Error, /indirect call signature mismatch/);
|
||||
assertEq(e4.call(2), 13);
|
||||
|
||||
var m = new Module(textToBinary(`(module
|
||||
@ -164,7 +164,7 @@ var call = evalText(`(module
|
||||
)`).exports.call;
|
||||
assertEq(call(0), 0);
|
||||
assertEq(call(1), 1);
|
||||
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(2), Error, /indirect call signature mismatch/);
|
||||
|
||||
var call = evalText(`(module
|
||||
(type $A (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
|
||||
@ -188,5 +188,5 @@ var call = evalText(`(module
|
||||
)`).exports.call;
|
||||
assertEq(call(0), 42);
|
||||
for (var i = 1; i < 7; i++)
|
||||
assertErrorMessage(() => call(i), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(i), Error, /indirect call signature mismatch/);
|
||||
assertErrorMessage(() => call(7), Error, /out-of-range/);
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
#include "vm/TypeInference-inl.h"
|
||||
|
||||
using mozilla::Maybe;
|
||||
|
||||
namespace js {
|
||||
@ -898,7 +900,7 @@ JitcodeGlobalEntry::IonEntry::mark(JSTracer* trc)
|
||||
iter != optsAllTypes_->end(); iter++)
|
||||
{
|
||||
if (ShouldMarkProvider::ShouldMark(&iter->type)) {
|
||||
TypeSet::MarkTypeUnbarriered(trc, &iter->type, "jitcodeglobaltable-ionentry-type");
|
||||
iter->type.trace(trc);
|
||||
markedAny = true;
|
||||
}
|
||||
if (iter->hasAllocationSite() && ShouldMarkProvider::ShouldMark(&iter->script)) {
|
||||
|
@ -2818,11 +2818,14 @@ MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc, const wasm::Cal
|
||||
}
|
||||
|
||||
loadPtr(Address(scratch, offsetof(wasm::ExternalTableElem, tls)), WasmTlsReg);
|
||||
branchTest32(Assembler::Zero, WasmTlsReg, WasmTlsReg, wasm::JumpTarget::IndirectCallToNull);
|
||||
|
||||
loadWasmPinnedRegsFromTls();
|
||||
|
||||
loadPtr(Address(scratch, offsetof(wasm::ExternalTableElem, code)), scratch);
|
||||
} else {
|
||||
loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
|
||||
branchTest32(Assembler::Zero, scratch, scratch, wasm::JumpTarget::IndirectCallToNull);
|
||||
}
|
||||
|
||||
call(desc, scratch);
|
||||
|
@ -285,6 +285,7 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic*
|
||||
Operand srcAddr(ptr, int32_t(mir->base().asValue()) + int32_t(offset));
|
||||
switch (accessType) {
|
||||
case Scalar::Int8: masm.movsblWithPatch(srcAddr, out.gpr()); break;
|
||||
case Scalar::Uint8Clamped:
|
||||
case Scalar::Uint8: masm.movzblWithPatch(srcAddr, out.gpr()); break;
|
||||
case Scalar::Int16: masm.movswlWithPatch(srcAddr, out.gpr()); break;
|
||||
case Scalar::Uint16: masm.movzwlWithPatch(srcAddr, out.gpr()); break;
|
||||
|
@ -204,7 +204,7 @@ MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal E
|
||||
MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand")
|
||||
MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id")
|
||||
MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function")
|
||||
MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode")
|
||||
MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
|
||||
MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch statement")
|
||||
MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
|
||||
MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
|
||||
@ -348,7 +348,8 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_WARN, "Successfully compiled
|
||||
MSG_DEF(JSMSG_WASM_FAIL, 1, JSEXN_TYPEERR, "wasm error: {0}")
|
||||
MSG_DEF(JSMSG_WASM_DECODE_FAIL, 2, JSEXN_TYPEERR, "wasm validation error at offset {0}: {1}")
|
||||
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
|
||||
MSG_DEF(JSMSG_WASM_BAD_IND_CALL, 0, JSEXN_ERR, "bad wasm indirect call")
|
||||
MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL, 0, JSEXN_ERR, "indirect call to null")
|
||||
MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG, 0, JSEXN_ERR, "indirect call signature mismatch")
|
||||
MSG_DEF(JSMSG_WASM_BAD_GROW, 0, JSEXN_ERR, "failed to grow memory")
|
||||
MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be an ArrayBuffer or typed array object")
|
||||
MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module")
|
||||
|
@ -6,11 +6,70 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscompartment.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
static bool
|
||||
ConstructCCW(JSContext* cx, const JSClass* globalClasp,
|
||||
JS::HandleObject global1, JS::MutableHandleObject wrapper,
|
||||
JS::MutableHandleObject global2, JS::MutableHandleObject wrappee)
|
||||
{
|
||||
if (!global1) {
|
||||
fprintf(stderr, "null initial global");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Define a second global in a different zone.
|
||||
JS::CompartmentOptions options;
|
||||
global2.set(JS_NewGlobalObject(cx, globalClasp, nullptr,
|
||||
JS::FireOnNewGlobalHook, options));
|
||||
if (!global2) {
|
||||
fprintf(stderr, "failed to create second global");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This should always be false, regardless.
|
||||
if (global1->compartment() == global2->compartment()) {
|
||||
fprintf(stderr, "second global claims to be in global1's compartment");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This checks that the API obeys the implicit zone request.
|
||||
if (global1->zone() == global2->zone()) {
|
||||
fprintf(stderr, "global2 is in global1's zone");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Define an object in compartment 2, that is wrapped by a CCW into compartment 1.
|
||||
{
|
||||
JSAutoCompartment ac(cx, global2);
|
||||
wrappee.set(JS_NewPlainObject(cx));
|
||||
if (wrappee->compartment() != global2->compartment()) {
|
||||
fprintf(stderr, "wrappee in wrong compartment");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wrapper.set(wrappee);
|
||||
if (!JS_WrapObject(cx, wrapper)) {
|
||||
fprintf(stderr, "failed to wrap");
|
||||
return false;
|
||||
}
|
||||
if (wrappee == wrapper) {
|
||||
fprintf(stderr, "expected wrapping");
|
||||
return false;
|
||||
}
|
||||
if (wrapper->compartment() != global1->compartment()) {
|
||||
fprintf(stderr, "wrapper in wrong compartment");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class CCWTestTracer : public JS::CallbackTracer {
|
||||
void onChild(const JS::GCCellPtr& thing) override {
|
||||
numberOfThingsTraced++;
|
||||
@ -42,35 +101,31 @@ class CCWTestTracer : public JS::CallbackTracer {
|
||||
|
||||
BEGIN_TEST(testTracingIncomingCCWs)
|
||||
{
|
||||
// Get two globals, in two different zones.
|
||||
#ifdef JS_GC_ZEAL
|
||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
||||
JS_SetGCZeal(cx, 0, 100);
|
||||
#endif
|
||||
JS_GC(cx);
|
||||
|
||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||
CHECK(global1);
|
||||
JS::CompartmentOptions options;
|
||||
JS::RootedObject global2(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
|
||||
JS::FireOnNewGlobalHook, options));
|
||||
CHECK(global2);
|
||||
CHECK(global1->compartment() != global2->compartment());
|
||||
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
|
||||
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
|
||||
JS_GC(cx);
|
||||
CHECK(!js::gc::IsInsideNursery(wrappee));
|
||||
CHECK(!js::gc::IsInsideNursery(wrapper));
|
||||
|
||||
// Define an object in one compartment, that is wrapped by a CCW in another
|
||||
// compartment.
|
||||
|
||||
JS::RootedObject obj(cx, JS_NewPlainObject(cx));
|
||||
CHECK(obj->compartment() == global1->compartment());
|
||||
|
||||
JSAutoCompartment ac(cx, global2);
|
||||
JS::RootedObject wrapper(cx, obj);
|
||||
CHECK(JS_WrapObject(cx, &wrapper));
|
||||
JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
|
||||
CHECK(JS_SetProperty(cx, global2, "ccw", v));
|
||||
CHECK(JS_SetProperty(cx, global1, "ccw", v));
|
||||
|
||||
// Ensure that |TraceIncomingCCWs| finds the object wrapped by the CCW.
|
||||
|
||||
JS::CompartmentSet compartments;
|
||||
CHECK(compartments.init());
|
||||
CHECK(compartments.put(global1->compartment()));
|
||||
CHECK(compartments.put(global2->compartment()));
|
||||
|
||||
void* thing = obj.get();
|
||||
void* thing = wrappee.get();
|
||||
CCWTestTracer trc(cx, &thing, JS::TraceKind::Object);
|
||||
JS::TraceIncomingCCWs(&trc, compartments);
|
||||
CHECK(trc.numberOfThingsTraced == 1);
|
||||
@ -80,6 +135,147 @@ BEGIN_TEST(testTracingIncomingCCWs)
|
||||
}
|
||||
END_TEST(testTracingIncomingCCWs)
|
||||
|
||||
static size_t
|
||||
countWrappers(JSCompartment* comp)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront())
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
BEGIN_TEST(testDeadNurseryCCW)
|
||||
{
|
||||
#ifdef JS_GC_ZEAL
|
||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
||||
JS_SetGCZeal(cx, 0, 100);
|
||||
#endif
|
||||
JS_GC(cx);
|
||||
|
||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
|
||||
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrapper));
|
||||
|
||||
// Now let the obj and wrapper die.
|
||||
wrappee = wrapper = nullptr;
|
||||
|
||||
// Now a GC should clear the CCW.
|
||||
CHECK(countWrappers(global1->compartment()) == 1);
|
||||
cx->gc.evictNursery();
|
||||
CHECK(countWrappers(global1->compartment()) == 0);
|
||||
|
||||
// Check for corruption of the CCW table by doing a full GC to force sweeping.
|
||||
JS_GC(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testDeadNurseryCCW)
|
||||
|
||||
BEGIN_TEST(testLiveNurseryCCW)
|
||||
{
|
||||
#ifdef JS_GC_ZEAL
|
||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
||||
JS_SetGCZeal(cx, 0, 100);
|
||||
#endif
|
||||
JS_GC(cx);
|
||||
|
||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
|
||||
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrapper));
|
||||
|
||||
// Now a GC should not kill the CCW.
|
||||
CHECK(countWrappers(global1->compartment()) == 1);
|
||||
cx->gc.evictNursery();
|
||||
CHECK(countWrappers(global1->compartment()) == 1);
|
||||
|
||||
CHECK(!js::gc::IsInsideNursery(wrappee));
|
||||
CHECK(!js::gc::IsInsideNursery(wrapper));
|
||||
|
||||
// Check for corruption of the CCW table by doing a full GC to force sweeping.
|
||||
JS_GC(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testLiveNurseryCCW)
|
||||
|
||||
BEGIN_TEST(testLiveNurseryWrapperCCW)
|
||||
{
|
||||
#ifdef JS_GC_ZEAL
|
||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
||||
JS_SetGCZeal(cx, 0, 100);
|
||||
#endif
|
||||
JS_GC(cx);
|
||||
|
||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
|
||||
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrapper));
|
||||
|
||||
// The wrapper contains a strong reference to the wrappee, so just dropping
|
||||
// the reference to the wrappee will not drop the CCW table entry as long
|
||||
// as the wrapper is held strongly. Thus, the minor collection here must
|
||||
// tenure both the wrapper and the wrappee and keep both in the table.
|
||||
wrappee = nullptr;
|
||||
|
||||
// Now a GC should not kill the CCW.
|
||||
CHECK(countWrappers(global1->compartment()) == 1);
|
||||
cx->gc.evictNursery();
|
||||
CHECK(countWrappers(global1->compartment()) == 1);
|
||||
|
||||
CHECK(!js::gc::IsInsideNursery(wrapper));
|
||||
|
||||
// Check for corruption of the CCW table by doing a full GC to force sweeping.
|
||||
JS_GC(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testLiveNurseryWrapperCCW)
|
||||
|
||||
BEGIN_TEST(testLiveNurseryWrappeeCCW)
|
||||
{
|
||||
#ifdef JS_GC_ZEAL
|
||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
||||
JS_SetGCZeal(cx, 0, 100);
|
||||
#endif
|
||||
JS_GC(cx);
|
||||
|
||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
|
||||
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrappee));
|
||||
CHECK(js::gc::IsInsideNursery(wrapper));
|
||||
|
||||
// Let the wrapper die. The wrapper should drop from the table when we GC,
|
||||
// even though there are other non-cross-compartment edges to it.
|
||||
wrapper = nullptr;
|
||||
|
||||
// Now a GC should not kill the CCW.
|
||||
CHECK(countWrappers(global1->compartment()) == 1);
|
||||
cx->gc.evictNursery();
|
||||
CHECK(countWrappers(global1->compartment()) == 0);
|
||||
|
||||
CHECK(!js::gc::IsInsideNursery(wrappee));
|
||||
|
||||
// Check for corruption of the CCW table by doing a full GC to force sweeping.
|
||||
JS_GC(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testLiveNurseryWrappeeCCW)
|
||||
|
||||
BEGIN_TEST(testIncrementalRoots)
|
||||
{
|
||||
JSRuntime* rt = cx->runtime();
|
||||
|
@ -191,6 +191,11 @@ JSObject* newCCW(JS::HandleObject sourceZone, JS::HandleObject destZone)
|
||||
if (!JS_WrapObject(cx, &object))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// In order to test the SCC algorithm, we need the wrapper/wrappee to be
|
||||
// tenured.
|
||||
cx->gc.evictNursery();
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
@ -233,12 +233,6 @@ JSCompartment::checkWrapperMapAfterMovingGC()
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
struct IsInsideNurseryFunctor {
|
||||
template <class T> bool operator()(T tp) { return IsInsideNursery(*tp); }
|
||||
};
|
||||
} // namespace (anonymous)
|
||||
|
||||
bool
|
||||
JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
|
||||
const js::Value& wrapper)
|
||||
@ -246,21 +240,7 @@ JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
|
||||
MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
|
||||
MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
|
||||
|
||||
/* There's no point allocating wrappers in the nursery since we will tenure them anyway. */
|
||||
MOZ_ASSERT(!IsInsideNursery(static_cast<gc::Cell*>(wrapper.toGCThing())));
|
||||
|
||||
bool isNuseryKey =
|
||||
const_cast<CrossCompartmentKey&>(wrapped).applyToWrapped(IsInsideNurseryFunctor()) ||
|
||||
const_cast<CrossCompartmentKey&>(wrapped).applyToDebugger(IsInsideNurseryFunctor());
|
||||
|
||||
if (isNuseryKey && !nurseryCCKeys.append(wrapped)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper))) {
|
||||
if (isNuseryKey)
|
||||
nurseryCCKeys.popBack();
|
||||
if (!crossCompartmentWrappers.put(wrapped, wrapper)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
@ -606,16 +586,6 @@ JSCompartment::trace(JSTracer* trc)
|
||||
varNames_.trace(trc);
|
||||
}
|
||||
|
||||
struct TraceFunctor {
|
||||
JSTracer* trc_;
|
||||
const char* name_;
|
||||
TraceFunctor(JSTracer *trc, const char* name)
|
||||
: trc_(trc), name_(name) {}
|
||||
template <class T> void operator()(T* t) {
|
||||
TraceManuallyBarrieredEdge(trc_, t, name_);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
|
||||
{
|
||||
@ -687,18 +657,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t
|
||||
if (nonSyntacticLexicalEnvironments_)
|
||||
nonSyntacticLexicalEnvironments_->trace(trc);
|
||||
|
||||
// In a minor GC we need to mark nursery objects that are the targets of
|
||||
// cross compartment wrappers.
|
||||
if (trc->runtime()->isHeapMinorCollecting()) {
|
||||
for (auto key : nurseryCCKeys) {
|
||||
CrossCompartmentKey prior = key;
|
||||
key.applyToWrapped(TraceFunctor(trc, "ccw wrapped"));
|
||||
key.applyToDebugger(TraceFunctor(trc, "ccw debugger"));
|
||||
crossCompartmentWrappers.rekeyIfMoved(prior, key);
|
||||
}
|
||||
nurseryCCKeys.clear();
|
||||
}
|
||||
|
||||
wasm.trace(trc);
|
||||
}
|
||||
|
||||
@ -724,12 +682,14 @@ JSCompartment::finishRoots()
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepAfterMinorGC()
|
||||
JSCompartment::sweepAfterMinorGC(JSTracer* trc)
|
||||
{
|
||||
globalWriteBarriered = 0;
|
||||
|
||||
if (innerViews.needsSweepAfterMinorGC())
|
||||
innerViews.sweepAfterMinorGC();
|
||||
|
||||
crossCompartmentWrappers.sweepAfterMinorGC(trc);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "asmjs/WasmCompartment.h"
|
||||
#include "builtin/RegExp.h"
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/NurseryAwareHashMap.h"
|
||||
#include "gc/Zone.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/PIC.h"
|
||||
@ -68,7 +69,7 @@ class DtoaCache {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct CrossCompartmentKey
|
||||
class CrossCompartmentKey
|
||||
{
|
||||
public:
|
||||
enum DebuggerObjectKind : uint8_t { DebuggerSource, DebuggerEnvironment, DebuggerObject,
|
||||
@ -169,6 +170,17 @@ struct CrossCompartmentKey
|
||||
return l.wrapped == k.wrapped;
|
||||
}
|
||||
};
|
||||
|
||||
bool isTenured() const {
|
||||
struct IsTenuredFunctor {
|
||||
using ReturnType = bool;
|
||||
ReturnType operator()(JSObject** tp) { return !IsInsideNursery(*tp); }
|
||||
ReturnType operator()(JSScript** tp) { return true; }
|
||||
ReturnType operator()(JSString** tp) { return true; }
|
||||
};
|
||||
return const_cast<CrossCompartmentKey*>(this)->applyToWrapped(IsTenuredFunctor());
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
bool needsSweep();
|
||||
|
||||
@ -177,8 +189,9 @@ struct CrossCompartmentKey
|
||||
WrappedType wrapped;
|
||||
};
|
||||
|
||||
using WrapperMap = GCRekeyableHashMap<CrossCompartmentKey, ReadBarrieredValue,
|
||||
CrossCompartmentKey::Hasher, SystemAllocPolicy>;
|
||||
|
||||
using WrapperMap = NurseryAwareHashMap<CrossCompartmentKey, JS::Value,
|
||||
CrossCompartmentKey::Hasher, SystemAllocPolicy>;
|
||||
|
||||
// We must ensure that all newly allocated JSObjects get their metadata
|
||||
// set. However, metadata builders may require the new object be in a sane
|
||||
@ -617,7 +630,7 @@ struct JSCompartment
|
||||
/* Whether to preserve JIT code on non-shrinking GCs. */
|
||||
bool preserveJitCode() { return creationOptions_.preserveJitCode(); }
|
||||
|
||||
void sweepAfterMinorGC();
|
||||
void sweepAfterMinorGC(JSTracer* trc);
|
||||
|
||||
void sweepCrossCompartmentWrappers();
|
||||
void sweepSavedStacks();
|
||||
@ -1066,4 +1079,11 @@ class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
template <>
|
||||
struct GCPolicy<js::CrossCompartmentKey> : public StructGCPolicy<js::CrossCompartmentKey> {
|
||||
static bool isTenured(const js::CrossCompartmentKey& key) { return key.isTenured(); }
|
||||
};
|
||||
} // namespace JS
|
||||
|
||||
#endif /* jscompartment_h */
|
||||
|
@ -110,7 +110,7 @@ JSCompartment::wrap(JSContext* cx, JS::MutableHandleValue vp)
|
||||
#ifdef DEBUG
|
||||
cacheResult = &p->value().get().toObject();
|
||||
#else
|
||||
vp.set(p->value());
|
||||
vp.set(p->value().get());
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
@ -3675,7 +3675,7 @@ InCrossCompartmentMap(JSObject* src, JS::GCCellPtr dst)
|
||||
*/
|
||||
for (JSCompartment::WrapperEnum e(srccomp); !e.empty(); e.popFront()) {
|
||||
if (e.front().mutableKey().applyToWrapped(IsDestComparatorFunctor(dst)) &&
|
||||
ToMarkable(e.front().value()) == src)
|
||||
ToMarkable(e.front().value().unbarrieredGet()) == src)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -3691,8 +3691,9 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const
|
||||
return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
|
||||
}
|
||||
|
||||
// Proxies have finalizers and are not nursery allocated.
|
||||
MOZ_ASSERT(!IsProxy(this));
|
||||
// Proxies that are CrossCompartmentWrappers may be nursery allocated.
|
||||
if (IsProxy(this))
|
||||
return as<ProxyObject>().allocKindForTenure();
|
||||
|
||||
// Unboxed plain objects are sized according to the data they store.
|
||||
if (is<UnboxedPlainObject>()) {
|
||||
|
@ -1129,9 +1129,15 @@ namespace js {
|
||||
inline gc::InitialHeap
|
||||
GetInitialHeap(NewObjectKind newKind, const Class* clasp)
|
||||
{
|
||||
if (newKind == NurseryAllocatedProxy) {
|
||||
MOZ_ASSERT(clasp->isProxy());
|
||||
MOZ_ASSERT(clasp->hasFinalize());
|
||||
MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
|
||||
return gc::DefaultHeap;
|
||||
}
|
||||
if (newKind != GenericObject)
|
||||
return gc::TenuredHeap;
|
||||
if (clasp->hasFinalize() && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
|
||||
if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp))
|
||||
return gc::TenuredHeap;
|
||||
return gc::DefaultHeap;
|
||||
}
|
||||
|
@ -350,7 +350,8 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(clasp->hasFinalize(), heap == js::gc::TenuredHeap ||
|
||||
(flags & JSCLASS_SKIP_NURSERY_FINALIZE));
|
||||
CanNurseryAllocateFinalizedClass(clasp) ||
|
||||
clasp->isProxy());
|
||||
MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), heap == js::gc::TenuredHeap);
|
||||
#endif
|
||||
|
||||
|
@ -215,6 +215,9 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
||||
|
||||
// Allocate CrossCompartmentWrappers in the nursery.
|
||||
virtual bool canNurseryAllocate() const override { return true; }
|
||||
|
||||
static const CrossCompartmentWrapper singleton;
|
||||
static const CrossCompartmentWrapper singletonWithPrototype;
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user