зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team.
This commit is contained in:
Коммит
2c26dad1a0
|
@ -19,11 +19,11 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="e5f4683183a1dec2cfdb21b76509819977e9d09c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -128,7 +128,7 @@
|
|||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="c3ee0c875393607430086f942950d1b3f496ab0e"/>
|
||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="ccccdcecc0ed9cff6b2445748903809ece2e04f3"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="f121175e1c0613f7d5c39be84b5a6330d8d664a0"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="dd94b2e17a146cb782d71933d25dcaa9c060e6ce"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="e0972cffef81e3833a5dad03a338651ebe55135f"/>
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="e5f4683183a1dec2cfdb21b76509819977e9d09c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -119,7 +119,7 @@
|
|||
<!-- Flame specific things -->
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="bbbeb3aeb0ae9f209eb8abe48a71f37e568b63af"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="1b3322cfd1179ea11fa5083d600717b65e5923e6"/>
|
||||
<project name="kernel/msm" path="kernel" revision="7158567fc83e7475f08db3adedc5df1ad6f54abd"/>
|
||||
<project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="4b7ae991637a216d745e154cd49b4db6ca55a19e"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "4393a8d719e7917f543cd8d906632c9b2164198e",
|
||||
"revision": "7e5b2c297555985ac76b069361ff252fe176a018",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
|
|
@ -34,7 +34,6 @@ ul {
|
|||
|
||||
#errorTitle {
|
||||
background: url("info.svg") left 0 no-repeat;
|
||||
background-size: 3em 3em;
|
||||
-moz-margin-start: -5em;
|
||||
-moz-padding-start: 5em;
|
||||
}
|
||||
|
|
|
@ -450,6 +450,13 @@ OMXVideoEncoder::SetBitrate(int32_t aKbps)
|
|||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
OMXVideoEncoder::RequestIDRFrame()
|
||||
{
|
||||
MOZ_ASSERT(mStarted, "Configure() should be called before RequestIDRFrame().");
|
||||
return mCodec->requestIDRFrame() == OK ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
|
||||
int aEncodedSampleRate)
|
||||
|
|
|
@ -282,6 +282,12 @@ public:
|
|||
*/
|
||||
nsresult GetCodecConfig(nsTArray<uint8_t>* aOutputBuf);
|
||||
|
||||
/**
|
||||
* Ask codec to generate an instantaneous decoding refresh (IDR) frame
|
||||
* (defined in ISO/IEC 14496-10).
|
||||
*/
|
||||
nsresult RequestIDRFrame();
|
||||
|
||||
protected:
|
||||
virtual status_t AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
|
||||
ABuffer* aData) MOZ_OVERRIDE;
|
||||
|
|
|
@ -504,35 +504,32 @@ static btrc_callbacks_t sBtAvrcpCallbacks = {
|
|||
* It is required to register a2dp callbacks before a2dp media task
|
||||
* starts up.
|
||||
*/
|
||||
bool
|
||||
BluetoothA2dpManager::Init()
|
||||
// static
|
||||
void
|
||||
BluetoothA2dpManager::InitA2dpInterface()
|
||||
{
|
||||
const bt_interface_t* btInf = GetBluetoothInterface();
|
||||
NS_ENSURE_TRUE(btInf, false);
|
||||
NS_ENSURE_TRUE_VOID(btInf);
|
||||
|
||||
sBtA2dpInterface = (btav_interface_t *)btInf->
|
||||
get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);
|
||||
NS_ENSURE_TRUE(sBtA2dpInterface, false);
|
||||
NS_ENSURE_TRUE_VOID(sBtA2dpInterface);
|
||||
|
||||
int ret = sBtA2dpInterface->init(&sBtA2dpCallbacks);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
BT_LOGR("Warning: failed to init a2dp module");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION > 17
|
||||
sBtAvrcpInterface = (btrc_interface_t *)btInf->
|
||||
get_profile_interface(BT_PROFILE_AV_RC_ID);
|
||||
NS_ENSURE_TRUE(sBtAvrcpInterface, false);
|
||||
NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
|
||||
|
||||
ret = sBtAvrcpInterface->init(&sBtAvrcpCallbacks);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
BT_LOGR("Warning: failed to init avrcp module");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BluetoothA2dpManager::~BluetoothA2dpManager()
|
||||
|
@ -602,12 +599,28 @@ BluetoothA2dpManager::Get()
|
|||
|
||||
// Create a new instance, register, and return
|
||||
BluetoothA2dpManager* manager = new BluetoothA2dpManager();
|
||||
NS_ENSURE_TRUE(manager->Init(), nullptr);
|
||||
|
||||
sBluetoothA2dpManager = manager;
|
||||
return sBluetoothA2dpManager;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothA2dpManager::DeinitA2dpInterface()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBtA2dpInterface) {
|
||||
sBtA2dpInterface->cleanup();
|
||||
sBtA2dpInterface = nullptr;
|
||||
}
|
||||
#if ANDROID_VERSION > 17
|
||||
if (sBtAvrcpInterface) {
|
||||
sBtAvrcpInterface->cleanup();
|
||||
sBtAvrcpInterface = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothA2dpManager::HandleShutdown()
|
||||
{
|
||||
|
@ -623,7 +636,7 @@ BluetoothA2dpManager::Connect(const nsAString& aDeviceAddress,
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!aDeviceAddress.IsEmpty());
|
||||
MOZ_ASSERT(aController && !mController);
|
||||
MOZ_ASSERT(aController);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs || sInShutdown) {
|
||||
|
|
|
@ -30,6 +30,8 @@ public:
|
|||
};
|
||||
|
||||
static BluetoothA2dpManager* Get();
|
||||
static void InitA2dpInterface();
|
||||
static void DeinitA2dpInterface();
|
||||
virtual ~BluetoothA2dpManager();
|
||||
|
||||
// A2DP-specific functions
|
||||
|
@ -60,7 +62,6 @@ public:
|
|||
private:
|
||||
class SinkPropertyChangedHandler;
|
||||
BluetoothA2dpManager();
|
||||
bool Init();
|
||||
void ResetA2dp();
|
||||
void ResetAvrcp();
|
||||
|
||||
|
|
|
@ -135,6 +135,26 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class CleanupTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
CleanupTask()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
|
||||
BluetoothHfpManager::DeinitHfpInterface();
|
||||
BluetoothA2dpManager::DeinitA2dpInterface();
|
||||
sBtInterface->cleanup();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Static callback functions
|
||||
*/
|
||||
|
@ -269,6 +289,10 @@ AdapterStateChangeCallback(bt_state_t aStatus)
|
|||
|
||||
sIsBtEnabled = (aStatus == BT_STATE_ON);
|
||||
|
||||
if (!sIsBtEnabled && NS_FAILED(NS_DispatchToMainThread(new CleanupTask()))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
|
||||
nsRefPtr<nsRunnable> runnable =
|
||||
new BluetoothService::ToggleBtAck(sIsBtEnabled);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
|
||||
|
@ -656,15 +680,29 @@ EnsureBluetoothHalLoad()
|
|||
}
|
||||
module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
|
||||
sBtDevice = (bluetooth_device_t *)device;
|
||||
NS_ENSURE_TRUE(sBtDevice, false);
|
||||
sBtInterface = sBtDevice->get_bluetooth_interface();
|
||||
NS_ENSURE_TRUE(sBtInterface, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EnableInternal()
|
||||
{
|
||||
int ret = sBtInterface->init(&sBluetoothCallbacks);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
BT_LOGR("Error while setting the callbacks");
|
||||
sBtInterface = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Register all the bluedroid callbacks before enable() get called
|
||||
// It is required to register a2dp callbacks before a2dp media task starts up.
|
||||
// If any interface cannot be initialized, turn on bluetooth core anyway.
|
||||
BluetoothHfpManager::InitHfpInterface();
|
||||
BluetoothA2dpManager::InitA2dpInterface();
|
||||
return sBtInterface->enable();
|
||||
}
|
||||
|
||||
static nsresult
|
||||
|
@ -683,7 +721,7 @@ StartStopGonkBluetooth(bool aShouldEnable)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
int ret = aShouldEnable ? sBtInterface->enable() : sBtInterface->disable();
|
||||
int ret = aShouldEnable ? EnableInternal() : sBtInterface->disable();
|
||||
NS_ENSURE_TRUE(ret == BT_STATUS_SUCCESS, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -727,11 +765,6 @@ BluetoothServiceBluedroid::BluetoothServiceBluedroid()
|
|||
BT_LOGR("Error! Failed to load bluedroid library.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Register all the bluedroid callbacks before enable() get called
|
||||
// It is required to register a2dp callbacks before a2dp media task starts up.
|
||||
BluetoothHfpManager::Get();
|
||||
BluetoothA2dpManager::Get();
|
||||
}
|
||||
|
||||
BluetoothServiceBluedroid::~BluetoothServiceBluedroid()
|
||||
|
|
|
@ -395,8 +395,6 @@ BluetoothHfpManager::Init()
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
NS_ENSURE_TRUE(InitHfpInterface(), false);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, false);
|
||||
|
||||
|
@ -430,11 +428,12 @@ BluetoothHfpManager::Init()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
// static
|
||||
void
|
||||
BluetoothHfpManager::InitHfpInterface()
|
||||
{
|
||||
const bt_interface_t* btInf = GetBluetoothInterface();
|
||||
NS_ENSURE_TRUE(btInf, false);
|
||||
NS_ENSURE_TRUE_VOID(btInf);
|
||||
|
||||
if (sBluetoothHfpInterface) {
|
||||
sBluetoothHfpInterface->cleanup();
|
||||
|
@ -443,13 +442,11 @@ BluetoothHfpManager::InitHfpInterface()
|
|||
|
||||
bthf_interface_t *interface = (bthf_interface_t *)
|
||||
btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID);
|
||||
NS_ENSURE_TRUE(interface, false);
|
||||
NS_ENSURE_TRUE_VOID(interface);
|
||||
|
||||
NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
|
||||
interface->init(&sBluetoothHfpCallbacks), false);
|
||||
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
||||
interface->init(&sBluetoothHfpCallbacks));
|
||||
sBluetoothHfpInterface = interface;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BluetoothHfpManager::~BluetoothHfpManager()
|
||||
|
@ -468,9 +465,9 @@ BluetoothHfpManager::~BluetoothHfpManager()
|
|||
}
|
||||
|
||||
hal::UnregisterBatteryObserver(this);
|
||||
DeinitHfpInterface();
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothHfpManager::DeinitHfpInterface()
|
||||
{
|
||||
|
@ -1045,13 +1042,14 @@ BluetoothHfpManager::UpdatePhoneCIND(uint32_t aCallIndex)
|
|||
void
|
||||
BluetoothHfpManager::UpdateDeviceCIND()
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
||||
if (sBluetoothHfpInterface) {
|
||||
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
||||
sBluetoothHfpInterface->device_status_notification(
|
||||
(bthf_network_state_t) mService,
|
||||
(bthf_service_type_t) mRoam,
|
||||
mSignal,
|
||||
mBattChg));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
|
|
@ -84,6 +84,8 @@ public:
|
|||
|
||||
static BluetoothHfpManager* Get();
|
||||
virtual ~BluetoothHfpManager();
|
||||
static void InitHfpInterface();
|
||||
static void DeinitHfpInterface();
|
||||
|
||||
bool ConnectSco();
|
||||
bool DisconnectSco();
|
||||
|
@ -134,8 +136,6 @@ private:
|
|||
|
||||
BluetoothHfpManager();
|
||||
bool Init();
|
||||
bool InitHfpInterface();
|
||||
void DeinitHfpInterface();
|
||||
|
||||
void HandleShutdown();
|
||||
void HandleVolumeChanged(const nsAString& aData);
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
USING_BLUETOOTH_NAMESPACE
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::ipc;
|
||||
using mozilla::TimeDuration;
|
||||
using mozilla::TimeStamp;
|
||||
|
||||
namespace {
|
||||
// Sending system message "bluetooth-opp-update-progress" every 50kb
|
||||
|
@ -53,6 +55,10 @@ static const uint32_t kPutRequestHeaderSize = 6;
|
|||
*/
|
||||
static const uint32_t kPutRequestAppendHeaderSize = 5;
|
||||
|
||||
// The default timeout we permit to wait for SDP updating if we can't get
|
||||
// service channel.
|
||||
static const double kSdpUpdatingTimeoutMs = 3000.0;
|
||||
|
||||
StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
|
||||
static bool sInShutdown = false;
|
||||
}
|
||||
|
@ -1546,10 +1552,18 @@ BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
|
|||
if (aChannel < 0) {
|
||||
if (mNeedsUpdatingSdpRecords) {
|
||||
mNeedsUpdatingSdpRecords = false;
|
||||
mLastServiceChannelCheck = TimeStamp::Now();
|
||||
bs->UpdateSdpRecords(aDeviceAddress, this);
|
||||
} else {
|
||||
TimeDuration duration = TimeStamp::Now() - mLastServiceChannelCheck;
|
||||
// Refresh SDP records until it gets valid service channel
|
||||
// unless timeout is hit.
|
||||
if (duration.ToMilliseconds() < kSdpUpdatingTimeoutMs) {
|
||||
bs->UpdateSdpRecords(aDeviceAddress, this);
|
||||
} else {
|
||||
OnSocketConnectError(mSocket);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -215,6 +215,10 @@ private:
|
|||
// is called.
|
||||
nsRefPtr<BluetoothSocket> mRfcommSocket;
|
||||
nsRefPtr<BluetoothSocket> mL2capSocket;
|
||||
|
||||
// This holds the time when OPP manager fail to get service channel and
|
||||
// prepare to refresh SDP records.
|
||||
mozilla::TimeStamp mLastServiceChannelCheck;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
|
|
@ -227,8 +227,6 @@ NetworkManager.prototype = {
|
|||
gNetworkService.removeHostRoutes(network.name);
|
||||
gNetworkService.addHostRoute(network);
|
||||
}
|
||||
// Add extra host route. For example, mms proxy or mmsc.
|
||||
this.setExtraHostRoute(network);
|
||||
// Dun type is a special case where we add the default route to a
|
||||
// secondary table.
|
||||
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
|
||||
|
@ -240,6 +238,12 @@ NetworkManager.prototype = {
|
|||
gNetworkService.removeDefaultRoute(network);
|
||||
this.setAndConfigureActive();
|
||||
#ifdef MOZ_B2G_RIL
|
||||
// Resolve and add extra host route. For example, mms proxy or mmsc.
|
||||
// IMPORTANT: The offline state of DNSService will be set implicitly in
|
||||
// setAndConfigureActive() by modifying Services.io.offline.
|
||||
// Always setExtraHostRoute() after setAndConfigureActive().
|
||||
this.setExtraHostRoute(network);
|
||||
|
||||
// Update data connection when Wifi connected/disconnected
|
||||
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
|
||||
for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
|
||||
|
@ -684,7 +688,9 @@ NetworkManager.prototype = {
|
|||
retval.push(hostnameIps.getNextAddrAsString());
|
||||
debug("Found IP at: " + JSON.stringify(retval));
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
debug("Failed to resolve '" + hostname + "', exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
|
|
@ -82,12 +82,17 @@ private:
|
|||
namespace js {
|
||||
|
||||
template <class UncompiledT>
|
||||
struct GCMethods<nsXBLMaybeCompiled<UncompiledT> > : public GCMethods<JSObject *>
|
||||
struct GCMethods<nsXBLMaybeCompiled<UncompiledT> >
|
||||
{
|
||||
typedef struct GCMethods<JSObject *> Base;
|
||||
|
||||
static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); }
|
||||
|
||||
/*
|
||||
* No implementation of kind() is provided to prevent
|
||||
* Root<nsXBLMaybeCompiled<UncompiledT>> from being used.
|
||||
*/
|
||||
|
||||
static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function)
|
||||
{
|
||||
return function.IsCompiled() && Base::poisoned(function.GetJSFunction());
|
||||
|
|
|
@ -277,12 +277,12 @@ struct AutoPaintSetup {
|
|||
mPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
|
||||
SkPaint temp;
|
||||
temp.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
|
||||
temp.setAlpha(U8CPU(aOptions.mAlpha*255));
|
||||
temp.setAlpha(U8CPU(aOptions.mAlpha*255+0.5));
|
||||
//TODO: Get a rect here
|
||||
mCanvas->saveLayer(nullptr, &temp);
|
||||
mNeedsRestore = true;
|
||||
} else {
|
||||
mPaint.setAlpha(U8CPU(aOptions.mAlpha*255.0));
|
||||
mPaint.setAlpha(U8CPU(aOptions.mAlpha*255.0+0.5));
|
||||
mAlpha = aOptions.mAlpha;
|
||||
}
|
||||
mPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
|
||||
|
|
|
@ -26,65 +26,12 @@ APZCCallbackHelper::HasValidPresShellId(nsIDOMWindowUtils* aUtils,
|
|||
return NS_SUCCEEDED(rv) && aMetrics.mPresShellId == presShellId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands a given rectangle to the next tile boundary. Note, this will
|
||||
* expand the rectangle if it is already on tile boundaries.
|
||||
*/
|
||||
static CSSRect ExpandDisplayPortToTileBoundaries(
|
||||
const CSSRect& aDisplayPort,
|
||||
const CSSToLayerScale& aLayerPixelsPerCSSPixel)
|
||||
{
|
||||
// Convert the given rect to layer coordinates so we can inflate to tile
|
||||
// boundaries (layer space corresponds to texture pixel space here).
|
||||
LayerRect displayPortInLayerSpace = aDisplayPort * aLayerPixelsPerCSSPixel;
|
||||
|
||||
// Inflate the rectangle by 1 so that we always push to the next tile
|
||||
// boundary. This is desirable to stop from having a rectangle with a
|
||||
// moving origin occasionally being smaller when it coincidentally lines
|
||||
// up to tile boundaries.
|
||||
displayPortInLayerSpace.Inflate(1);
|
||||
|
||||
// Now nudge the rectangle to the nearest equal or larger tile boundary.
|
||||
int32_t tileWidth = gfxPrefs::LayersTileWidth();
|
||||
int32_t tileHeight = gfxPrefs::LayersTileHeight();
|
||||
gfxFloat left = tileWidth * floor(displayPortInLayerSpace.x / tileWidth);
|
||||
gfxFloat right = tileWidth * ceil(displayPortInLayerSpace.XMost() / tileWidth);
|
||||
gfxFloat top = tileHeight * floor(displayPortInLayerSpace.y / tileHeight);
|
||||
gfxFloat bottom = tileHeight * ceil(displayPortInLayerSpace.YMost() / tileHeight);
|
||||
|
||||
displayPortInLayerSpace = LayerRect(left, top, right - left, bottom - top);
|
||||
CSSRect displayPort = displayPortInLayerSpace / aLayerPixelsPerCSSPixel;
|
||||
|
||||
return displayPort;
|
||||
}
|
||||
|
||||
static void
|
||||
MaybeAlignAndClampDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics,
|
||||
AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
|
||||
const CSSPoint& aActualScrollOffset)
|
||||
{
|
||||
// Correct the display-port by the difference between the requested scroll
|
||||
// offset and the resulting scroll offset after setting the requested value.
|
||||
if (!aFrameMetrics.GetUseDisplayPortMargins()) {
|
||||
CSSRect& displayPort = aFrameMetrics.mDisplayPort;
|
||||
displayPort += aFrameMetrics.GetScrollOffset() - aActualScrollOffset;
|
||||
|
||||
// Expand the display port to the next tile boundaries, if tiled thebes layers
|
||||
// are enabled.
|
||||
if (gfxPrefs::LayersTilesEnabled()) {
|
||||
// We don't use LayersPixelsPerCSSPixel() here as mCumulativeResolution on
|
||||
// this FrameMetrics may be incorrect (and is about to be reset by mZoom).
|
||||
displayPort =
|
||||
ExpandDisplayPortToTileBoundaries(displayPort + aActualScrollOffset,
|
||||
aFrameMetrics.GetZoom() *
|
||||
ScreenToLayerScale(1.0))
|
||||
- aActualScrollOffset;
|
||||
}
|
||||
|
||||
// Finally, clamp the display port to the expanded scrollable rect.
|
||||
CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
|
||||
displayPort = scrollableRect.Intersect(displayPort + aActualScrollOffset)
|
||||
- aActualScrollOffset;
|
||||
} else {
|
||||
LayerPoint shift =
|
||||
(aFrameMetrics.GetScrollOffset() - aActualScrollOffset) *
|
||||
aFrameMetrics.LayersPixelsPerCSSPixel();
|
||||
|
@ -94,22 +41,15 @@ MaybeAlignAndClampDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics,
|
|||
margins.top -= shift.y;
|
||||
margins.bottom += shift.y;
|
||||
aFrameMetrics.SetDisplayPortMargins(margins);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RecenterDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics)
|
||||
{
|
||||
if (!aFrameMetrics.GetUseDisplayPortMargins()) {
|
||||
CSSSize compositionSize = aFrameMetrics.CalculateCompositedSizeInCssPixels();
|
||||
aFrameMetrics.mDisplayPort.x = (compositionSize.width - aFrameMetrics.mDisplayPort.width) / 2;
|
||||
aFrameMetrics.mDisplayPort.y = (compositionSize.height - aFrameMetrics.mDisplayPort.height) / 2;
|
||||
} else {
|
||||
LayerMargin margins = aFrameMetrics.GetDisplayPortMargins();
|
||||
margins.right = margins.left = margins.LeftRight() / 2;
|
||||
margins.top = margins.bottom = margins.TopBottom() / 2;
|
||||
aFrameMetrics.SetDisplayPortMargins(margins);
|
||||
}
|
||||
}
|
||||
|
||||
static CSSPoint
|
||||
|
@ -161,6 +101,7 @@ APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils,
|
|||
{
|
||||
// Precondition checks
|
||||
MOZ_ASSERT(aUtils);
|
||||
MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
|
||||
if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
|
||||
return;
|
||||
}
|
||||
|
@ -180,7 +121,11 @@ APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils,
|
|||
bool scrollUpdated = false;
|
||||
CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), scrollUpdated);
|
||||
|
||||
if (!scrollUpdated) {
|
||||
if (scrollUpdated) {
|
||||
// Correct the display port due to the difference between mScrollOffset and the
|
||||
// actual scroll offset.
|
||||
AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
|
||||
} else {
|
||||
// For whatever reason we couldn't update the scroll offset on the scroll frame,
|
||||
// which means the data APZ used for its displayport calculation is stale. Fall
|
||||
// back to a sane default behaviour. Note that we don't tile-align the recentered
|
||||
|
@ -190,11 +135,6 @@ APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils,
|
|||
RecenterDisplayPort(aMetrics);
|
||||
}
|
||||
|
||||
// Correct the display port due to the difference between mScrollOffset and the
|
||||
// actual scroll offset, possibly align it to tile boundaries (if tiled layers are
|
||||
// enabled), and clamp it to the scrollable rect.
|
||||
MaybeAlignAndClampDisplayPort(aMetrics, actualScrollOffset);
|
||||
|
||||
aMetrics.SetScrollOffset(actualScrollOffset);
|
||||
|
||||
// The mZoom variable on the frame metrics stores the CSS-to-screen scale for this
|
||||
|
@ -221,13 +161,7 @@ APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils,
|
|||
if (!element) {
|
||||
return;
|
||||
}
|
||||
if (!aMetrics.GetUseDisplayPortMargins()) {
|
||||
aUtils->SetDisplayPortForElement(aMetrics.mDisplayPort.x,
|
||||
aMetrics.mDisplayPort.y,
|
||||
aMetrics.mDisplayPort.width,
|
||||
aMetrics.mDisplayPort.height,
|
||||
element, 0);
|
||||
} else {
|
||||
|
||||
gfx::IntSize alignment = gfxPrefs::LayersTilesEnabled()
|
||||
? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) :
|
||||
gfx::IntSize(0, 0);
|
||||
|
@ -245,7 +179,6 @@ APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils,
|
|||
baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(),
|
||||
baseCSS.height * nsPresContext::AppUnitsPerCSSPixel());
|
||||
nsLayoutUtils::SetDisplayPortBaseIfNotSet(content, base);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -254,6 +187,7 @@ APZCCallbackHelper::UpdateSubFrame(nsIContent* aContent,
|
|||
{
|
||||
// Precondition checks
|
||||
MOZ_ASSERT(aContent);
|
||||
MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
|
||||
if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
|
||||
return;
|
||||
}
|
||||
|
@ -272,17 +206,11 @@ APZCCallbackHelper::UpdateSubFrame(nsIContent* aContent,
|
|||
|
||||
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
|
||||
if (element) {
|
||||
if (!scrollUpdated) {
|
||||
if (scrollUpdated) {
|
||||
AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
|
||||
} else {
|
||||
RecenterDisplayPort(aMetrics);
|
||||
}
|
||||
MaybeAlignAndClampDisplayPort(aMetrics, actualScrollOffset);
|
||||
if (!aMetrics.GetUseDisplayPortMargins()) {
|
||||
utils->SetDisplayPortForElement(aMetrics.mDisplayPort.x,
|
||||
aMetrics.mDisplayPort.y,
|
||||
aMetrics.mDisplayPort.width,
|
||||
aMetrics.mDisplayPort.height,
|
||||
element, 0);
|
||||
} else {
|
||||
gfx::IntSize alignment = gfxPrefs::LayersTilesEnabled()
|
||||
? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) :
|
||||
gfx::IntSize(0, 0);
|
||||
|
@ -301,7 +229,6 @@ APZCCallbackHelper::UpdateSubFrame(nsIContent* aContent,
|
|||
baseCSS.height * nsPresContext::AppUnitsPerCSSPixel());
|
||||
nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base);
|
||||
}
|
||||
}
|
||||
|
||||
aMetrics.SetScrollOffset(actualScrollOffset);
|
||||
}
|
||||
|
|
|
@ -394,9 +394,6 @@ BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
|
|||
nsIntRegion invalidRegionSafe;
|
||||
invalidRegionSafe.And(aInvalidRegion, intRect);
|
||||
|
||||
// FIXME: Redraw the whole screen in every frame to work around bug 972728.
|
||||
invalidRegionSafe = intRect;
|
||||
|
||||
nsIntRect invalidRect = invalidRegionSafe.GetBounds();
|
||||
mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
|
||||
mInvalidRegion = invalidRegionSafe;
|
||||
|
|
|
@ -71,18 +71,17 @@ CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
|
|||
flags |= TextureFlags::NEEDS_Y_FLIP;
|
||||
}
|
||||
|
||||
gfx::SurfaceFormat surfaceFormat = gfx::ImageFormatToSurfaceFormat(format);
|
||||
if (aLayer->IsGLLayer()) {
|
||||
// We want a cairo backend here as we don't want to be copying into
|
||||
// an accelerated backend and we like LockBits to work. This is currently
|
||||
// the most effective way to make this work.
|
||||
mBuffer = CreateBufferTextureClient(gfx::ImageFormatToSurfaceFormat(format),
|
||||
flags,
|
||||
BackendType::CAIRO);
|
||||
mBuffer = CreateBufferTextureClient(surfaceFormat, flags, BackendType::CAIRO);
|
||||
} else {
|
||||
mBuffer = CreateTextureClientForDrawing(gfx::ImageFormatToSurfaceFormat(format),
|
||||
flags,
|
||||
gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(),
|
||||
aSize);
|
||||
// XXX - We should use CreateTextureClientForDrawing, but we first need
|
||||
// to use double buffering.
|
||||
mBuffer = CreateBufferTextureClient(surfaceFormat, flags,
|
||||
gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
|
||||
}
|
||||
MOZ_ASSERT(mBuffer->CanExposeDrawTarget());
|
||||
mBuffer->AllocateForSurface(aSize);
|
||||
|
|
|
@ -139,6 +139,7 @@ class GCRuntime
|
|||
void markGrayReferencesInCurrentGroup();
|
||||
void beginSweepPhase(bool lastGC);
|
||||
void findZoneGroups();
|
||||
bool findZoneEdgesForWeakMaps();
|
||||
void getNextZoneGroup();
|
||||
void endMarkingZoneGroup();
|
||||
void beginSweepingZoneGroup();
|
||||
|
|
|
@ -63,6 +63,11 @@ Zone::~Zone()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool Zone::init()
|
||||
{
|
||||
return gcZoneGroupEdges.init();
|
||||
}
|
||||
|
||||
void
|
||||
Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define gc_Zone_h
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
@ -147,6 +148,7 @@ struct Zone : public JS::shadow::Zone,
|
|||
bool gcScheduled;
|
||||
GCState gcState;
|
||||
bool gcPreserveCode;
|
||||
mozilla::DebugOnly<unsigned> gcLastZoneGroupIndex;
|
||||
|
||||
public:
|
||||
bool isCollecting() const {
|
||||
|
@ -228,6 +230,16 @@ struct Zone : public JS::shadow::Zone,
|
|||
return gcState == Finished;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* For testing purposes, return the index of the zone group which this zone
|
||||
* was swept in in the last GC.
|
||||
*/
|
||||
unsigned lastZoneGroupIndex() {
|
||||
return gcLastZoneGroupIndex;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This is updated by both the main and GC helper threads. */
|
||||
mozilla::Atomic<size_t, mozilla::ReleaseAcquire> gcBytes;
|
||||
|
||||
|
@ -272,12 +284,23 @@ struct Zone : public JS::shadow::Zone,
|
|||
/* This compartment's gray roots. */
|
||||
js::Vector<js::GrayRoot, 0, js::SystemAllocPolicy> gcGrayRoots;
|
||||
|
||||
/*
|
||||
* A set of edges from this zone to other zones.
|
||||
*
|
||||
* This is used during GC while calculating zone groups to record edges that
|
||||
* can't be determined by examining this zone by itself.
|
||||
*/
|
||||
typedef js::HashSet<Zone *, js::DefaultHasher<Zone *>, js::SystemAllocPolicy> ZoneSet;
|
||||
ZoneSet gcZoneGroupEdges;
|
||||
|
||||
/* Per-zone data for use by an embedder. */
|
||||
void *data;
|
||||
|
||||
Zone(JSRuntime *rt);
|
||||
~Zone();
|
||||
|
||||
bool init();
|
||||
|
||||
void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
|
||||
|
||||
void discardJitCode(js::FreeOp *fop);
|
||||
|
|
|
@ -867,13 +867,16 @@ NativeRegExpMacroAssembler::LoadCurrentCharacterUnchecked(int cp_offset, int cha
|
|||
{
|
||||
IonSpew(SPEW_PREFIX "LoadCurrentCharacterUnchecked(%d, %d)", cp_offset, characters);
|
||||
|
||||
JS_ASSERT(characters == 1);
|
||||
if (mode_ == ASCII) {
|
||||
MOZ_ASSUME_UNREACHABLE("Ascii loading not implemented");
|
||||
} else {
|
||||
JS_ASSERT(mode_ == JSCHAR);
|
||||
masm.load16ZeroExtend(BaseIndex(input_end_pointer, current_position, TimesOne, cp_offset * sizeof(jschar)),
|
||||
current_character);
|
||||
JS_ASSERT(characters <= 2);
|
||||
BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset * sizeof(jschar));
|
||||
if (characters == 2)
|
||||
masm.load32(address, current_character);
|
||||
else
|
||||
masm.load16ZeroExtend(address, current_character);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1235,9 +1238,7 @@ NativeRegExpMacroAssembler::CheckSpecialCharacterClass(jschar type, Label* on_no
|
|||
bool
|
||||
NativeRegExpMacroAssembler::CanReadUnaligned()
|
||||
{
|
||||
// XXX Bug 1006799 should this be enabled? Unaligned loads can be slow even
|
||||
// on platforms where they are supported.
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t
|
||||
|
|
|
@ -1992,26 +1992,11 @@ ICCompare_String::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
|
||||
GeneralRegisterSet regs(availableGeneralRegs(2));
|
||||
Register scratchReg = regs.takeAny();
|
||||
// x86 doesn't have the luxury of a second scratch.
|
||||
Register scratchReg2;
|
||||
if (regs.empty()) {
|
||||
scratchReg2 = BaselineStubReg;
|
||||
masm.push(BaselineStubReg);
|
||||
} else {
|
||||
scratchReg2 = regs.takeAny();
|
||||
}
|
||||
JS_ASSERT(scratchReg2 != scratchReg);
|
||||
|
||||
Label inlineCompareFailed;
|
||||
masm.compareStrings(op, left, right, scratchReg2, scratchReg, &inlineCompareFailed);
|
||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratchReg2, R0);
|
||||
if (scratchReg2 == BaselineStubReg)
|
||||
masm.pop(BaselineStubReg);
|
||||
masm.compareStrings(op, left, right, scratchReg, &failure);
|
||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratchReg, R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&inlineCompareFailed);
|
||||
if (scratchReg2 == BaselineStubReg)
|
||||
masm.pop(BaselineStubReg);
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
|
@ -4224,8 +4209,8 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler &masm)
|
|||
Label skipAtomize;
|
||||
|
||||
// If string is already an atom, skip the atomize.
|
||||
masm.branchTestPtr(Assembler::NonZero,
|
||||
Address(strExtract, JSString::offsetOfLengthAndFlags()),
|
||||
masm.branchTest32(Assembler::NonZero,
|
||||
Address(strExtract, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::ATOM_BIT),
|
||||
&skipAtomize);
|
||||
|
||||
|
@ -4429,23 +4414,18 @@ ICGetElem_String::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
// Unbox string in R0.
|
||||
Register str = masm.extractString(R0, ExtractTemp0);
|
||||
|
||||
// Load string lengthAndFlags
|
||||
Address lengthAndFlagsAddr(str, JSString::offsetOfLengthAndFlags());
|
||||
masm.loadPtr(lengthAndFlagsAddr, scratchReg);
|
||||
|
||||
// Check for non-linear strings.
|
||||
masm.branchTest32(Assembler::Zero, scratchReg, Imm32(JSString::FLAGS_MASK), &failure);
|
||||
masm.branchIfRope(str, &failure);
|
||||
|
||||
// Unbox key.
|
||||
Register key = masm.extractInt32(R1, ExtractTemp1);
|
||||
|
||||
// Extract length and bounds check.
|
||||
masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), scratchReg);
|
||||
masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
|
||||
// Bounds check.
|
||||
masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()),
|
||||
key, &failure);
|
||||
|
||||
// Get char code.
|
||||
Address charsAddr(str, JSString::offsetOfChars());
|
||||
masm.loadPtr(charsAddr, scratchReg);
|
||||
masm.loadStringChars(str, scratchReg);
|
||||
masm.load16ZeroExtend(BaseIndex(scratchReg, key, TimesTwo, 0), scratchReg);
|
||||
|
||||
// Check if char code >= UNIT_STATIC_LIMIT.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "jslibmath.h"
|
||||
#include "jsmath.h"
|
||||
|
@ -3350,6 +3351,7 @@ CodeGenerator::emitDebugResultChecks(LInstruction *ins)
|
|||
default:
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3612,6 +3614,90 @@ CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ShouldInitFixedSlots(LInstruction *lir, JSObject *templateObj)
|
||||
{
|
||||
// Look for StoreFixedSlot instructions following an object allocation
|
||||
// that write to this object before a GC is triggered or this object is
|
||||
// passed to a VM call. If all fixed slots will be initialized, the
|
||||
// allocation code doesn't need to set the slots to |undefined|.
|
||||
|
||||
uint32_t nfixed = templateObj->numUsedFixedSlots();
|
||||
if (nfixed == 0)
|
||||
return false;
|
||||
|
||||
// Only optimize if all fixed slots are initially |undefined|, so that we
|
||||
// can assume incremental pre-barriers are not necessary. See also the
|
||||
// comment below.
|
||||
for (uint32_t slot = 0; slot < nfixed; slot++) {
|
||||
if (!templateObj->getSlot(slot).isUndefined())
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keep track of the fixed slots that are initialized. initializedSlots is
|
||||
// a bit mask with a bit for each slot.
|
||||
MOZ_ASSERT(nfixed <= JSObject::MAX_FIXED_SLOTS);
|
||||
static_assert(JSObject::MAX_FIXED_SLOTS <= 32, "Slot bits must fit in 32 bits");
|
||||
uint32_t initializedSlots = 0;
|
||||
uint32_t numInitialized = 0;
|
||||
|
||||
MInstruction *allocMir = lir->mirRaw()->toInstruction();
|
||||
MBasicBlock *block = allocMir->block();
|
||||
|
||||
// Skip the allocation instruction.
|
||||
MInstructionIterator iter = block->begin(allocMir);
|
||||
MOZ_ASSERT(*iter == allocMir);
|
||||
iter++;
|
||||
|
||||
while (true) {
|
||||
for (; iter != block->end(); iter++) {
|
||||
if (iter->isNop() || iter->isConstant() || iter->isPostWriteBarrier()) {
|
||||
// These instructions won't trigger a GC or read object slots.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->isStoreFixedSlot()) {
|
||||
MStoreFixedSlot *store = iter->toStoreFixedSlot();
|
||||
if (store->object() != allocMir)
|
||||
return true;
|
||||
|
||||
// We may not initialize this object slot on allocation, so the
|
||||
// pre-barrier could read uninitialized memory. Simply disable
|
||||
// the barrier for this store: the object was just initialized
|
||||
// so the barrier is not necessary.
|
||||
store->setNeedsBarrier(false);
|
||||
|
||||
uint32_t slot = store->slot();
|
||||
MOZ_ASSERT(slot < nfixed);
|
||||
if ((initializedSlots & (1 << slot)) == 0) {
|
||||
numInitialized++;
|
||||
initializedSlots |= (1 << slot);
|
||||
|
||||
if (numInitialized == nfixed) {
|
||||
// All fixed slots will be initialized.
|
||||
MOZ_ASSERT(mozilla::CountPopulation32(initializedSlots) == nfixed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->isGoto()) {
|
||||
block = iter->toGoto()->target();
|
||||
if (block->numPredecessors() != 1)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Unhandled instruction, assume it bails or reads object slots.
|
||||
return true;
|
||||
}
|
||||
iter = block->begin();
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Shouldn't get here");
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitNewObject(LNewObject *lir)
|
||||
{
|
||||
|
@ -3627,7 +3713,9 @@ CodeGenerator::visitNewObject(LNewObject *lir)
|
|||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
|
||||
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject);
|
||||
masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry(),
|
||||
initFixedSlots);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
|
@ -3662,7 +3750,9 @@ CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
|
|||
if (!ool)
|
||||
return false;
|
||||
|
||||
masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry());
|
||||
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
|
||||
masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
|
||||
initFixedSlots);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
|
@ -3688,7 +3778,9 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir)
|
|||
return false;
|
||||
|
||||
// Inline call object creation, using the OOL path only for tricky cases.
|
||||
masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry());
|
||||
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
|
||||
masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
|
||||
initFixedSlots);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
|
@ -4038,7 +4130,9 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
|
|||
|
||||
// Initialize based on the templateObject.
|
||||
masm.bind(ool->rejoin());
|
||||
masm.initGCThing(objReg, tempReg, templateObject);
|
||||
|
||||
bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject);
|
||||
masm.initGCThing(objReg, tempReg, templateObject, initFixedSlots);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -4580,7 +4674,7 @@ static const VMFunctionsModal StringsNotEqualInfo = VMFunctionsModal(
|
|||
|
||||
bool
|
||||
CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
|
||||
Register output, Register temp)
|
||||
Register output)
|
||||
{
|
||||
JS_ASSERT(lir->isCompareS() || lir->isCompareStrictS());
|
||||
|
||||
|
@ -4595,7 +4689,7 @@ CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register
|
|||
if (!ool)
|
||||
return false;
|
||||
|
||||
masm.compareStrings(op, left, right, output, temp, ool->entry());
|
||||
masm.compareStrings(op, left, right, output, ool->entry());
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
|
@ -4610,7 +4704,6 @@ CodeGenerator::visitCompareStrictS(LCompareStrictS *lir)
|
|||
const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs);
|
||||
Register right = ToRegister(lir->right());
|
||||
Register output = ToRegister(lir->output());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox());
|
||||
|
||||
Label string, done;
|
||||
|
@ -4621,7 +4714,7 @@ CodeGenerator::visitCompareStrictS(LCompareStrictS *lir)
|
|||
|
||||
masm.bind(&string);
|
||||
Register left = masm.extractString(leftV, tempToUnbox);
|
||||
if (!emitCompareS(lir, op, left, right, output, temp))
|
||||
if (!emitCompareS(lir, op, left, right, output))
|
||||
return false;
|
||||
|
||||
masm.bind(&done);
|
||||
|
@ -4636,9 +4729,8 @@ CodeGenerator::visitCompareS(LCompareS *lir)
|
|||
Register left = ToRegister(lir->left());
|
||||
Register right = ToRegister(lir->right());
|
||||
Register output = ToRegister(lir->output());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
|
||||
return emitCompareS(lir, op, left, right, output, temp);
|
||||
return emitCompareS(lir, op, left, right, output);
|
||||
}
|
||||
|
||||
typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, bool *);
|
||||
|
@ -5066,9 +5158,8 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||
}
|
||||
|
||||
// Store lengthAndFlags.
|
||||
JS_STATIC_ASSERT(JSString::ROPE_FLAGS == 0);
|
||||
masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2);
|
||||
masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags()));
|
||||
masm.store32(Imm32(JSString::ROPE_FLAGS), Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Store left and right nodes.
|
||||
masm.storePtr(lhs, Address(output, JSRope::offsetOfLeft()));
|
||||
|
@ -5087,12 +5178,9 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||
|
||||
// State: lhs length in temp1, result length in temp2.
|
||||
|
||||
// Ensure both strings are linear (flags != 0).
|
||||
JS_STATIC_ASSERT(JSString::ROPE_FLAGS == 0);
|
||||
masm.branchTestPtr(Assembler::Zero, Address(lhs, JSString::offsetOfLengthAndFlags()),
|
||||
Imm32(JSString::FLAGS_MASK), &failure);
|
||||
masm.branchTestPtr(Assembler::Zero, Address(rhs, JSString::offsetOfLengthAndFlags()),
|
||||
Imm32(JSString::FLAGS_MASK), &failure);
|
||||
// Ensure both strings are linear.
|
||||
masm.branchIfRope(lhs, &failure);
|
||||
masm.branchIfRope(rhs, &failure);
|
||||
|
||||
// Allocate a JSFatInlineString.
|
||||
switch (mode) {
|
||||
|
@ -5110,14 +5198,12 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
// Set lengthAndFlags.
|
||||
masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2);
|
||||
masm.orPtr(Imm32(JSString::FIXED_FLAGS), temp2);
|
||||
masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags()));
|
||||
// Set length and flags.
|
||||
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
// Set chars pointer, keep in temp2 for copy loop below.
|
||||
masm.computeEffectiveAddress(Address(output, JSFatInlineString::offsetOfInlineStorage()), temp2);
|
||||
masm.storePtr(temp2, Address(output, JSFatInlineString::offsetOfChars()));
|
||||
// Load chars pointer in temp2.
|
||||
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()), temp2);
|
||||
|
||||
{
|
||||
// We use temp3 in this block, which in parallel execution also holds
|
||||
|
@ -5129,12 +5215,12 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||
// Copy lhs chars. Temp1 still holds the lhs length. Note that this
|
||||
// advances temp2 to point to the next char. Note that this also
|
||||
// repurposes the lhs register.
|
||||
masm.loadPtr(Address(lhs, JSString::offsetOfChars()), lhs);
|
||||
masm.loadStringChars(lhs, lhs);
|
||||
CopyStringChars(masm, temp2, lhs, temp1, temp3);
|
||||
|
||||
// Copy rhs chars.
|
||||
masm.loadStringLength(rhs, temp1);
|
||||
masm.loadPtr(Address(rhs, JSString::offsetOfChars()), rhs);
|
||||
masm.loadStringChars(rhs, rhs);
|
||||
CopyStringChars(masm, temp2, rhs, temp1, temp3);
|
||||
|
||||
if (mode == ParallelExecution)
|
||||
|
@ -5241,12 +5327,9 @@ CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
|
|||
if (!ool)
|
||||
return false;
|
||||
|
||||
Address lengthAndFlagsAddr(str, JSString::offsetOfLengthAndFlags());
|
||||
masm.branchTest32(Assembler::Zero, lengthAndFlagsAddr, Imm32(JSString::FLAGS_MASK), ool->entry());
|
||||
masm.branchIfRope(str, ool->entry());
|
||||
|
||||
// getChars
|
||||
Address charsAddr(str, JSString::offsetOfChars());
|
||||
masm.loadPtr(charsAddr, output);
|
||||
masm.loadStringChars(str, output);
|
||||
masm.load16ZeroExtend(BaseIndex(output, index, TimesTwo, 0), output);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
|
@ -6445,10 +6528,18 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
|
|||
|
||||
// Check to make sure we didn't have a mid-build invalidation. If so, we
|
||||
// will trickle to jit::Compile() and return Method_Skipped.
|
||||
uint32_t useCount = script->getUseCount();
|
||||
types::RecompileInfo recompileInfo;
|
||||
if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
|
||||
return true;
|
||||
|
||||
// IonMonkey could have inferred better type information during
|
||||
// compilation. Since adding the new information to the actual type
|
||||
// information can reset the usecount, increase it back to what it was
|
||||
// before.
|
||||
if (useCount > script->getUseCount())
|
||||
script->incUseCount(useCount - script->getUseCount());
|
||||
|
||||
uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
|
||||
? frameDepth_
|
||||
: FrameSizeClass::FromDepth(frameDepth_).frameSize();
|
||||
|
@ -8579,18 +8670,31 @@ CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
|
|||
typedef bool (*RecompileFn)(JSContext *);
|
||||
static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
|
||||
|
||||
typedef bool (*ForcedRecompileFn)(JSContext *);
|
||||
static const VMFunction ForcedRecompileFnInfo = FunctionInfo<ForcedRecompileFn>(ForcedRecompile);
|
||||
|
||||
bool
|
||||
CodeGenerator::visitRecompileCheck(LRecompileCheck *ins)
|
||||
{
|
||||
Label done;
|
||||
Register tmp = ToRegister(ins->scratch());
|
||||
OutOfLineCode *ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
|
||||
OutOfLineCode *ool;
|
||||
if (ins->mir()->forceRecompilation())
|
||||
ool = oolCallVM(ForcedRecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
|
||||
else
|
||||
ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
|
||||
|
||||
// Check if usecount is high enough.
|
||||
masm.movePtr(ImmPtr(ins->mir()->script()->addressOfUseCount()), tmp);
|
||||
Address ptr(tmp, 0);
|
||||
AbsoluteAddress useCount = AbsoluteAddress(ins->mir()->script()->addressOfUseCount());
|
||||
if (ins->mir()->increaseUseCount()) {
|
||||
masm.load32(useCount, tmp);
|
||||
masm.add32(Imm32(1), tmp);
|
||||
masm.branch32(Assembler::BelowOrEqual, ptr, Imm32(ins->mir()->recompileThreshold()), &done);
|
||||
masm.store32(tmp, useCount);
|
||||
masm.branch32(Assembler::BelowOrEqual, tmp, Imm32(ins->mir()->recompileThreshold()), &done);
|
||||
} else {
|
||||
masm.branch32(Assembler::BelowOrEqual, useCount, Imm32(ins->mir()->recompileThreshold()),
|
||||
&done);
|
||||
}
|
||||
|
||||
// Check if not yet recompiling.
|
||||
CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), tmp);
|
||||
|
|
|
@ -203,8 +203,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool visitModD(LModD *ins);
|
||||
bool visitMinMaxI(LMinMaxI *lir);
|
||||
bool visitBinaryV(LBinaryV *lir);
|
||||
bool emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
|
||||
Register output, Register temp);
|
||||
bool emitCompareS(LInstruction *lir, JSOp op, Register left, Register right, Register output);
|
||||
bool visitCompareS(LCompareS *lir);
|
||||
bool visitCompareStrictS(LCompareStrictS *lir);
|
||||
bool visitCompareVM(LCompareVM *lir);
|
||||
|
|
|
@ -1515,6 +1515,19 @@ OptimizeMIR(MIRGenerator *mir)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Make loops contiguious. We do this after GVN/UCE and range analysis,
|
||||
// which can remove CFG edges, exposing more blocks that can be moved.
|
||||
{
|
||||
AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous);
|
||||
if (!MakeLoopsContiguous(graph))
|
||||
return false;
|
||||
IonSpewPass("Make loops contiguous");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Make loops contiguous"))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Passes after this point must not move instructions; these analyses
|
||||
// depend on knowing the final order in which instructions will execute.
|
||||
|
||||
|
@ -2064,7 +2077,7 @@ GetOptimizationLevel(HandleScript script, jsbytecode *pc, ExecutionMode executio
|
|||
|
||||
static MethodStatus
|
||||
Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
|
||||
bool constructing, ExecutionMode executionMode)
|
||||
bool constructing, ExecutionMode executionMode, bool forceRecompile = false)
|
||||
{
|
||||
JS_ASSERT(jit::IsIonEnabled(cx));
|
||||
JS_ASSERT(jit::IsBaselineEnabled(cx));
|
||||
|
@ -2101,35 +2114,17 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
|
|||
if (!scriptIon->method())
|
||||
return Method_CantCompile;
|
||||
|
||||
MethodStatus failedState = Method_Compiled;
|
||||
|
||||
// If we keep failing to enter the script due to an OSR pc mismatch,
|
||||
// recompile with the right pc.
|
||||
if (osrPc && script->ionScript()->osrPc() != osrPc) {
|
||||
uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
|
||||
if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile)
|
||||
return Method_Skipped;
|
||||
|
||||
failedState = Method_Skipped;
|
||||
}
|
||||
|
||||
// Don't recompile/overwrite higher optimized code,
|
||||
// with a lower optimization level.
|
||||
if (optimizationLevel < scriptIon->optimizationLevel())
|
||||
return failedState;
|
||||
|
||||
if (optimizationLevel == scriptIon->optimizationLevel() &&
|
||||
(!osrPc || script->ionScript()->osrPc() == osrPc))
|
||||
{
|
||||
return failedState;
|
||||
}
|
||||
if (optimizationLevel <= scriptIon->optimizationLevel() && !forceRecompile)
|
||||
return Method_Compiled;
|
||||
|
||||
// Don't start compiling if already compiling
|
||||
if (scriptIon->isRecompiling())
|
||||
return failedState;
|
||||
return Method_Compiled;
|
||||
|
||||
if (osrPc)
|
||||
script->ionScript()->resetOsrPcMismatchCounter();
|
||||
scriptIon->resetOsrPcMismatchCounter();
|
||||
|
||||
recompile = true;
|
||||
}
|
||||
|
@ -2148,11 +2143,8 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
|
|||
}
|
||||
|
||||
// Compilation succeeded or we invalidated right away or an inlining/alloc abort
|
||||
if (HasIonScript(script, executionMode)) {
|
||||
if (osrPc && script->ionScript()->osrPc() != osrPc)
|
||||
return Method_Skipped;
|
||||
if (HasIonScript(script, executionMode))
|
||||
return Method_Compiled;
|
||||
}
|
||||
return Method_Skipped;
|
||||
}
|
||||
|
||||
|
@ -2191,19 +2183,35 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
|
|||
return Method_CantCompile;
|
||||
}
|
||||
|
||||
// By default a recompilation doesn't happen on osr mismatch.
|
||||
// Decide if we want to force a recompilation if this happens too much.
|
||||
bool force = false;
|
||||
if (script->hasIonScript() && pc != script->ionScript()->osrPc()) {
|
||||
uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
|
||||
if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile)
|
||||
return Method_Skipped;
|
||||
force = true;
|
||||
}
|
||||
|
||||
// Attempt compilation.
|
||||
// - Returns Method_Compiled if the right ionscript is present
|
||||
// (Meaning it was present or a sequantial compile finished)
|
||||
// - Returns Method_Skipped if pc doesn't match
|
||||
// (This means a background thread compilation with that pc could have started or not.)
|
||||
RootedScript rscript(cx, script);
|
||||
MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution);
|
||||
MethodStatus status =
|
||||
Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution, force);
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(cx, script);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Return the compilation was skipped when the osr pc wasn't adjusted.
|
||||
// This can happen when there was still an IonScript available and a
|
||||
// background compilation started, but hasn't finished yet.
|
||||
// Or when we didn't force a recompile.
|
||||
if (pc != script->ionScript()->osrPc())
|
||||
return Method_Skipped;
|
||||
|
||||
return Method_Compiled;
|
||||
}
|
||||
|
||||
|
@ -2314,14 +2322,14 @@ jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFram
|
|||
|
||||
MethodStatus
|
||||
jit::Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
|
||||
bool constructing)
|
||||
bool constructing, bool force)
|
||||
{
|
||||
JS_ASSERT(script->hasIonScript());
|
||||
if (script->ionScript()->isRecompiling())
|
||||
return Method_Compiled;
|
||||
|
||||
MethodStatus status =
|
||||
Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution);
|
||||
Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution, force);
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(cx, script);
|
||||
|
|
|
@ -95,7 +95,7 @@ MethodStatus CanEnterInParallel(JSContext *cx, HandleScript script);
|
|||
|
||||
MethodStatus
|
||||
Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
|
||||
bool constructing);
|
||||
bool constructing, bool force);
|
||||
|
||||
enum IonExecStatus
|
||||
{
|
||||
|
|
|
@ -2563,3 +2563,96 @@ jit::AnalyzeArgumentsUsage(JSContext *cx, JSScript *scriptArg)
|
|||
script->setNeedsArgsObj(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reorder the blocks in the loop starting at the given header to be contiguous.
|
||||
static void
|
||||
MakeLoopContiguous(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge, size_t numMarked)
|
||||
{
|
||||
MOZ_ASSERT(header->isMarked(), "Loop header is not part of loop");
|
||||
MOZ_ASSERT(backedge->isMarked(), "Loop backedge is not part of loop");
|
||||
|
||||
// If there are any blocks between the loop header and the loop backedge
|
||||
// that are not part of the loop, prepare to move them to the end. We keep
|
||||
// them in order, which preserves RPO.
|
||||
ReversePostorderIterator insertIter = graph.rpoBegin(backedge);
|
||||
insertIter++;
|
||||
MBasicBlock *insertPt = *insertIter;
|
||||
|
||||
// Visit all the blocks from the loop header to the loop backedge.
|
||||
size_t headerId = header->id();
|
||||
size_t inLoopId = headerId;
|
||||
size_t afterLoopId = inLoopId + numMarked;
|
||||
ReversePostorderIterator i = graph.rpoBegin(header);
|
||||
for (;;) {
|
||||
MBasicBlock *block = *i++;
|
||||
MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(),
|
||||
"Loop backedge should be last block in loop");
|
||||
|
||||
if (block->isMarked()) {
|
||||
// This block is in the loop.
|
||||
block->unmark();
|
||||
block->setId(inLoopId++);
|
||||
// If we've reached the loop backedge, we're done!
|
||||
if (block == backedge)
|
||||
break;
|
||||
} else {
|
||||
// This block is not in the loop. Move it to the end.
|
||||
graph.moveBlockBefore(insertPt, block);
|
||||
block->setId(afterLoopId++);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(header->id() == headerId, "Loop header id changed");
|
||||
MOZ_ASSERT(inLoopId == headerId + numMarked, "Wrong number of blocks kept in loop");
|
||||
MOZ_ASSERT(afterLoopId == (insertIter != graph.rpoEnd() ? insertPt->id() : graph.numBlocks()),
|
||||
"Wrong number of blocks moved out of loop");
|
||||
}
|
||||
|
||||
// Reorder the blocks in the graph so that loops are contiguous.
|
||||
bool
|
||||
jit::MakeLoopsContiguous(MIRGraph &graph)
|
||||
{
|
||||
MBasicBlock *osrBlock = graph.osrBlock();
|
||||
Vector<MBasicBlock *, 1, IonAllocPolicy> inlooplist(graph.alloc());
|
||||
|
||||
// Visit all loop headers (in any order).
|
||||
for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) {
|
||||
MBasicBlock *header = *i;
|
||||
if (!header->isLoopHeader())
|
||||
continue;
|
||||
|
||||
// Mark all the blocks in the loop by marking all blocks in a path
|
||||
// between the backedge and the loop header.
|
||||
MBasicBlock *backedge = header->backedge();
|
||||
size_t numMarked = 1;
|
||||
backedge->mark();
|
||||
if (!inlooplist.append(backedge))
|
||||
return false;
|
||||
do {
|
||||
MBasicBlock *block = inlooplist.popCopy();
|
||||
MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(),
|
||||
"Non-OSR predecessor of loop block not between header and backedge");
|
||||
if (block == header)
|
||||
continue;
|
||||
for (size_t p = 0; p < block->numPredecessors(); p++) {
|
||||
MBasicBlock *pred = block->getPredecessor(p);
|
||||
if (pred->isMarked())
|
||||
continue;
|
||||
// Ignore paths entering the loop in the middle from an OSR
|
||||
// entry. They won't pass through the loop header and they
|
||||
// aren't part of the loop.
|
||||
if (osrBlock && osrBlock->dominates(pred) && !osrBlock->dominates(header))
|
||||
continue;
|
||||
++numMarked;
|
||||
pred->mark();
|
||||
if (!inlooplist.append(pred))
|
||||
return false;
|
||||
}
|
||||
} while (!inlooplist.empty());
|
||||
|
||||
// Move all blocks between header and backedge that aren't marked to
|
||||
// the end of the loop, making the loop itself contiguous.
|
||||
MakeLoopContiguous(graph, header, backedge, numMarked);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ enum Observability {
|
|||
bool
|
||||
EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe);
|
||||
|
||||
bool
|
||||
MakeLoopsContiguous(MIRGraph &graph);
|
||||
|
||||
bool
|
||||
EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph);
|
||||
|
||||
|
|
|
@ -4176,7 +4176,9 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
|
|||
if (targetScript->getUseCount() < optimizationInfo().usesBeforeInlining() &&
|
||||
info().executionMode() != DefinitePropertiesAnalysis)
|
||||
{
|
||||
return DontInline(targetScript, "Vetoed: callee is insufficiently hot.");
|
||||
IonSpew(IonSpew_Inlining, "Cannot inline %s:%u: callee is insufficiently hot.",
|
||||
targetScript->filename(), targetScript->lineno());
|
||||
return InliningDecision_UseCountTooLow;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4211,6 +4213,7 @@ IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, Boo
|
|||
case InliningDecision_Error:
|
||||
return false;
|
||||
case InliningDecision_DontInline:
|
||||
case InliningDecision_UseCountTooLow:
|
||||
inlineable = false;
|
||||
break;
|
||||
case InliningDecision_Inline:
|
||||
|
@ -4331,6 +4334,8 @@ IonBuilder::inlineCallsite(ObjectVector &targets, ObjectVector &originals,
|
|||
return InliningStatus_Error;
|
||||
case InliningDecision_DontInline:
|
||||
return InliningStatus_NotInlined;
|
||||
case InliningDecision_UseCountTooLow:
|
||||
return InliningStatus_UseCountTooLow;
|
||||
case InliningDecision_Inline:
|
||||
break;
|
||||
}
|
||||
|
@ -4955,6 +4960,7 @@ IonBuilder::jsop_funcall(uint32_t argc)
|
|||
case InliningDecision_Error:
|
||||
return false;
|
||||
case InliningDecision_DontInline:
|
||||
case InliningDecision_UseCountTooLow:
|
||||
break;
|
||||
case InliningDecision_Inline:
|
||||
if (target->isInterpreted())
|
||||
|
@ -5095,6 +5101,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
|
|||
case InliningDecision_Error:
|
||||
return false;
|
||||
case InliningDecision_DontInline:
|
||||
case InliningDecision_UseCountTooLow:
|
||||
break;
|
||||
case InliningDecision_Inline:
|
||||
if (target->isInterpreted())
|
||||
|
@ -5165,6 +5172,13 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
|
|||
if (targets.length() == 1)
|
||||
target = &targets[0]->as<JSFunction>();
|
||||
|
||||
if (target && status == InliningStatus_UseCountTooLow) {
|
||||
MRecompileCheck *check = MRecompileCheck::New(alloc(), target->nonLazyScript(),
|
||||
optimizationInfo().usesBeforeInlining(),
|
||||
MRecompileCheck::RecompileCheck_Inlining);
|
||||
current->add(check);
|
||||
}
|
||||
|
||||
return makeCall(target, callInfo, hasClones);
|
||||
}
|
||||
|
||||
|
@ -6137,7 +6151,10 @@ IonBuilder::insertRecompileCheck()
|
|||
OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel);
|
||||
const OptimizationInfo *info = js_IonOptimizations.get(nextLevel);
|
||||
uint32_t useCount = info->usesBeforeCompile(topBuilder->script());
|
||||
current->add(MRecompileCheck::New(alloc(), topBuilder->script(), useCount));
|
||||
|
||||
MRecompileCheck *check = MRecompileCheck::New(alloc(), topBuilder->script(), useCount,
|
||||
MRecompileCheck::RecompileCheck_OptimizationLevel);
|
||||
current->add(check);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
|
@ -8869,6 +8886,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
|
|||
case InliningDecision_Error:
|
||||
return false;
|
||||
case InliningDecision_DontInline:
|
||||
case InliningDecision_UseCountTooLow:
|
||||
break;
|
||||
case InliningDecision_Inline:
|
||||
inlineable = true;
|
||||
|
@ -9285,6 +9303,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
|
|||
case InliningDecision_Error:
|
||||
return false;
|
||||
case InliningDecision_DontInline:
|
||||
case InliningDecision_UseCountTooLow:
|
||||
break;
|
||||
case InliningDecision_Inline:
|
||||
if (!inlineScriptedCall(callInfo, commonSetter))
|
||||
|
|
|
@ -633,6 +633,7 @@ class IonBuilder : public MIRGenerator
|
|||
{
|
||||
InliningStatus_Error,
|
||||
InliningStatus_NotInlined,
|
||||
InliningStatus_UseCountTooLow,
|
||||
InliningStatus_Inlined
|
||||
};
|
||||
|
||||
|
@ -640,7 +641,8 @@ class IonBuilder : public MIRGenerator
|
|||
{
|
||||
InliningDecision_Error,
|
||||
InliningDecision_Inline,
|
||||
InliningDecision_DontInline
|
||||
InliningDecision_DontInline,
|
||||
InliningDecision_UseCountTooLow
|
||||
};
|
||||
|
||||
static InliningDecision DontInline(JSScript *targetScript, const char *reason);
|
||||
|
|
|
@ -3008,12 +3008,12 @@ GetElementIC::attachGetProp(JSContext *cx, HandleScript outerScript, IonScript *
|
|||
|
||||
// The pointers are not equal, so if the input string is also an atom it
|
||||
// must be a different string.
|
||||
masm.loadPtr(Address(scratch, JSString::offsetOfLengthAndFlags()), scratch);
|
||||
masm.branchTest32(Assembler::NonZero, scratch, Imm32(JSString::ATOM_BIT), &failures);
|
||||
masm.branchTest32(Assembler::NonZero, Address(scratch, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::ATOM_BIT), &failures);
|
||||
|
||||
// Check the length.
|
||||
masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), scratch);
|
||||
masm.branch32(Assembler::NotEqual, scratch, Imm32(name->length()), &failures);
|
||||
masm.branch32(Assembler::NotEqual, Address(scratch, JSString::offsetOfLength()),
|
||||
Imm32(name->length()), &failures);
|
||||
|
||||
// We have a non-atomized string with the same length. For now call a helper
|
||||
// function to do the comparison.
|
||||
|
|
|
@ -591,14 +591,14 @@ MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj
|
|||
|
||||
void
|
||||
MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj,
|
||||
gc::InitialHeap initialHeap, Label *fail)
|
||||
gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots)
|
||||
{
|
||||
uint32_t nDynamicSlots = templateObj->numDynamicSlots();
|
||||
gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
|
||||
JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
|
||||
|
||||
allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail);
|
||||
initGCThing(obj, temp, templateObj);
|
||||
initGCThing(obj, temp, templateObj, initFixedSlots);
|
||||
}
|
||||
|
||||
|
||||
|
@ -742,7 +742,8 @@ FindStartOfUndefinedSlots(JSObject *templateObj, uint32_t nslots)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj)
|
||||
MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj,
|
||||
bool initFixedSlots)
|
||||
{
|
||||
// Slots of non-array objects are required to be initialized.
|
||||
// Use the values currently in the template object.
|
||||
|
@ -750,7 +751,7 @@ MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj)
|
|||
if (nslots == 0)
|
||||
return;
|
||||
|
||||
uint32_t nfixed = Min(templateObj->numFixedSlots(), nslots);
|
||||
uint32_t nfixed = templateObj->numUsedFixedSlots();
|
||||
uint32_t ndynamic = templateObj->numDynamicSlots();
|
||||
|
||||
// Attempt to group slot writes such that we minimize the amount of
|
||||
|
@ -767,8 +768,10 @@ MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj)
|
|||
copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined);
|
||||
|
||||
// Fill the rest of the fixed slots with undefined.
|
||||
if (initFixedSlots) {
|
||||
fillSlotsWithUndefined(Address(obj, JSObject::getFixedSlotOffset(startOfUndefined)), slots,
|
||||
startOfUndefined, nfixed);
|
||||
}
|
||||
|
||||
if (ndynamic) {
|
||||
// We are short one register to do this elegantly. Borrow the obj
|
||||
|
@ -781,7 +784,8 @@ MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj)
|
||||
MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj,
|
||||
bool initFixedSlots)
|
||||
{
|
||||
// Fast initialization of an empty object returned by allocateObject().
|
||||
|
||||
|
@ -818,7 +822,7 @@ MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj)
|
|||
} else {
|
||||
storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements()));
|
||||
|
||||
initGCSlots(obj, slots, templateObj);
|
||||
initGCSlots(obj, slots, templateObj, initFixedSlots);
|
||||
|
||||
if (templateObj->hasPrivate()) {
|
||||
uint32_t nfixed = templateObj->numFixedSlots();
|
||||
|
@ -830,7 +834,7 @@ MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj)
|
|||
|
||||
void
|
||||
MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
|
||||
Register temp, Label *fail)
|
||||
Label *fail)
|
||||
{
|
||||
JS_ASSERT(IsEqualityOp(op));
|
||||
|
||||
|
@ -842,28 +846,41 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register
|
|||
jump(&done);
|
||||
|
||||
bind(¬PointerEqual);
|
||||
loadPtr(Address(left, JSString::offsetOfLengthAndFlags()), result);
|
||||
loadPtr(Address(right, JSString::offsetOfLengthAndFlags()), temp);
|
||||
|
||||
Label notAtom;
|
||||
// Optimize the equality operation to a pointer compare for two atoms.
|
||||
Imm32 atomBit(JSString::ATOM_BIT);
|
||||
branchTest32(Assembler::Zero, result, atomBit, ¬Atom);
|
||||
branchTest32(Assembler::Zero, temp, atomBit, ¬Atom);
|
||||
branchTest32(Assembler::Zero, Address(left, JSString::offsetOfFlags()), atomBit, ¬Atom);
|
||||
branchTest32(Assembler::Zero, Address(right, JSString::offsetOfFlags()), atomBit, ¬Atom);
|
||||
|
||||
cmpPtrSet(JSOpToCondition(MCompare::Compare_String, op), left, right, result);
|
||||
jump(&done);
|
||||
|
||||
bind(¬Atom);
|
||||
// Strings of different length can never be equal.
|
||||
rshiftPtr(Imm32(JSString::LENGTH_SHIFT), result);
|
||||
rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp);
|
||||
branchPtr(Assembler::Equal, result, temp, fail);
|
||||
loadStringLength(left, result);
|
||||
branch32(Assembler::Equal, Address(right, JSString::offsetOfLength()), result, fail);
|
||||
move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), result);
|
||||
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::loadStringChars(Register str, Register dest)
|
||||
{
|
||||
Label isInline, done;
|
||||
branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::INLINE_CHARS_BIT), &isInline);
|
||||
|
||||
loadPtr(Address(str, JSString::offsetOfNonInlineChars()), dest);
|
||||
jump(&done);
|
||||
|
||||
bind(&isInline);
|
||||
computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()), dest);
|
||||
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail)
|
||||
{
|
||||
|
|
|
@ -377,8 +377,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
}
|
||||
|
||||
void loadStringLength(Register str, Register dest) {
|
||||
loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), dest);
|
||||
rshiftPtr(Imm32(JSString::LENGTH_SHIFT), dest);
|
||||
load32(Address(str, JSString::offsetOfLength()), dest);
|
||||
}
|
||||
|
||||
void loadStringChars(Register str, Register dest);
|
||||
|
||||
void branchIfRope(Register str, Label *label) {
|
||||
Address flags(str, JSString::offsetOfFlags());
|
||||
branch32(Assembler::Equal, flags, Imm32(JSString::ROPE_FLAGS), label);
|
||||
}
|
||||
|
||||
void loadSliceBounds(Register worker, Register dest) {
|
||||
|
@ -802,17 +808,18 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
void copySlotsFromTemplate(Register obj, const JSObject *templateObj,
|
||||
uint32_t start, uint32_t end);
|
||||
void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
|
||||
void initGCSlots(Register obj, Register temp, JSObject *templateObj);
|
||||
void initGCSlots(Register obj, Register temp, JSObject *templateObj, bool initFixedSlots);
|
||||
|
||||
public:
|
||||
void callMallocStub(size_t nbytes, Register result, Label *fail);
|
||||
void callFreeStub(Register slots);
|
||||
void createGCObject(Register result, Register temp, JSObject *templateObj,
|
||||
gc::InitialHeap initialHeap, Label *fail);
|
||||
gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true);
|
||||
|
||||
void newGCThing(Register result, Register temp, JSObject *templateObj,
|
||||
gc::InitialHeap initialHeap, Label *fail);
|
||||
void initGCThing(Register obj, Register temp, JSObject *templateObj);
|
||||
void initGCThing(Register obj, Register temp, JSObject *templateObj,
|
||||
bool initFixedSlots = true);
|
||||
|
||||
void newGCString(Register result, Register temp, Label *fail);
|
||||
void newGCFatInlineString(Register result, Register temp, Label *fail);
|
||||
|
@ -830,7 +837,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// Compares two strings for equality based on the JSOP.
|
||||
// This checks for identical pointers, atoms and length and fails for everything else.
|
||||
void compareStrings(JSOp op, Register left, Register right, Register result,
|
||||
Register temp, Label *fail);
|
||||
Label *fail);
|
||||
|
||||
// Checks the flags that signal that parallel code may need to interrupt or
|
||||
// abort. Branches to fail in that case.
|
||||
|
|
|
@ -1870,15 +1870,13 @@ class LCompareFAndBranch : public LControlInstructionHelper<2, 2, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LCompareS : public LInstructionHelper<1, 2, 1>
|
||||
class LCompareS : public LInstructionHelper<1, 2, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CompareS)
|
||||
LCompareS(const LAllocation &left, const LAllocation &right,
|
||||
const LDefinition &temp) {
|
||||
LCompareS(const LAllocation &left, const LAllocation &right) {
|
||||
setOperand(0, left);
|
||||
setOperand(1, right);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation *left() {
|
||||
|
@ -1887,24 +1885,19 @@ class LCompareS : public LInstructionHelper<1, 2, 1>
|
|||
const LAllocation *right() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
MCompare *mir() {
|
||||
return mir_->toCompare();
|
||||
}
|
||||
};
|
||||
|
||||
// strict-equality between value and string.
|
||||
class LCompareStrictS : public LInstructionHelper<1, BOX_PIECES + 1, 2>
|
||||
class LCompareStrictS : public LInstructionHelper<1, BOX_PIECES + 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CompareStrictS)
|
||||
LCompareStrictS(const LAllocation &rhs, const LDefinition &temp0,
|
||||
const LDefinition &temp1) {
|
||||
LCompareStrictS(const LAllocation &rhs, const LDefinition &temp) {
|
||||
setOperand(BOX_PIECES, rhs);
|
||||
setTemp(0, temp0);
|
||||
setTemp(1, temp1);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
static const size_t Lhs = 0;
|
||||
|
@ -1912,11 +1905,8 @@ class LCompareStrictS : public LInstructionHelper<1, BOX_PIECES + 1, 2>
|
|||
const LAllocation *right() {
|
||||
return getOperand(BOX_PIECES);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition *tempToUnbox() {
|
||||
return getTemp(1);
|
||||
return getTemp(0);
|
||||
}
|
||||
MCompare *mir() {
|
||||
return mir_->toCompare();
|
||||
|
|
|
@ -896,7 +896,7 @@ LIRGenerator::visitCompare(MCompare *comp)
|
|||
// LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
|
||||
// make sense and avoids confusion.
|
||||
if (comp->compareType() == MCompare::Compare_String) {
|
||||
LCompareS *lir = new(alloc()) LCompareS(useRegister(left), useRegister(right), temp());
|
||||
LCompareS *lir = new(alloc()) LCompareS(useRegister(left), useRegister(right));
|
||||
if (!define(lir, comp))
|
||||
return false;
|
||||
return assignSafepoint(lir, comp);
|
||||
|
@ -907,7 +907,7 @@ LIRGenerator::visitCompare(MCompare *comp)
|
|||
JS_ASSERT(left->type() == MIRType_Value);
|
||||
JS_ASSERT(right->type() == MIRType_String);
|
||||
|
||||
LCompareStrictS *lir = new(alloc()) LCompareStrictS(useRegister(right), temp(), tempToUnbox());
|
||||
LCompareStrictS *lir = new(alloc()) LCompareStrictS(useRegister(right), tempToUnbox());
|
||||
if (!useBox(lir, LCompareStrictS::Lhs, left))
|
||||
return false;
|
||||
if (!define(lir, comp))
|
||||
|
|
|
@ -7000,8 +7000,8 @@ class MStoreFixedSlot
|
|||
bool needsBarrier() const {
|
||||
return needsBarrier_;
|
||||
}
|
||||
void setNeedsBarrier() {
|
||||
needsBarrier_ = true;
|
||||
void setNeedsBarrier(bool needsBarrier = true) {
|
||||
needsBarrier_ = needsBarrier;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9869,21 +9869,45 @@ class MHasClass
|
|||
// outermost script (i.e. not the inlined script).
|
||||
class MRecompileCheck : public MNullaryInstruction
|
||||
{
|
||||
public:
|
||||
enum RecompileCheckType {
|
||||
RecompileCheck_OptimizationLevel,
|
||||
RecompileCheck_Inlining
|
||||
};
|
||||
|
||||
private:
|
||||
JSScript *script_;
|
||||
uint32_t recompileThreshold_;
|
||||
bool forceRecompilation_;
|
||||
bool increaseUseCount_;
|
||||
|
||||
MRecompileCheck(JSScript *script, uint32_t recompileThreshold)
|
||||
MRecompileCheck(JSScript *script, uint32_t recompileThreshold, RecompileCheckType type)
|
||||
: script_(script),
|
||||
recompileThreshold_(recompileThreshold)
|
||||
{
|
||||
switch (type) {
|
||||
case RecompileCheck_OptimizationLevel:
|
||||
forceRecompilation_ = false;
|
||||
increaseUseCount_ = true;
|
||||
break;
|
||||
case RecompileCheck_Inlining:
|
||||
forceRecompilation_ = true;
|
||||
increaseUseCount_ = false;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unexpected recompile check type");
|
||||
}
|
||||
|
||||
setGuard();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(RecompileCheck);
|
||||
|
||||
static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t useCount) {
|
||||
return new(alloc) MRecompileCheck(script_, useCount);
|
||||
static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_,
|
||||
uint32_t useCount, RecompileCheckType type)
|
||||
{
|
||||
return new(alloc) MRecompileCheck(script_, useCount, type);
|
||||
}
|
||||
|
||||
JSScript *script() const {
|
||||
|
@ -9894,6 +9918,14 @@ class MRecompileCheck : public MNullaryInstruction
|
|||
return recompileThreshold_;
|
||||
}
|
||||
|
||||
bool forceRecompilation() const {
|
||||
return forceRecompilation_;
|
||||
}
|
||||
|
||||
bool increaseUseCount() const {
|
||||
return increaseUseCount_;
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
|
|
@ -616,6 +616,11 @@ class MIRGraph
|
|||
blocks_.remove(block);
|
||||
blocks_.pushBack(block);
|
||||
}
|
||||
void moveBlockBefore(MBasicBlock *at, MBasicBlock *block) {
|
||||
JS_ASSERT(block->id());
|
||||
blocks_.remove(block);
|
||||
blocks_.insertBefore(at, block);
|
||||
}
|
||||
size_t numBlocks() const {
|
||||
return numBlocks_;
|
||||
}
|
||||
|
|
|
@ -1023,8 +1023,8 @@ StringReplace(JSContext *cx, HandleString string, HandleString pattern, HandleSt
|
|||
return rval.toString();
|
||||
}
|
||||
|
||||
bool
|
||||
Recompile(JSContext *cx)
|
||||
static bool
|
||||
RecompileImpl(JSContext *cx, bool force)
|
||||
{
|
||||
JS_ASSERT(cx->currentlyRunningInJit());
|
||||
JitActivationIterator activations(cx->runtime());
|
||||
|
@ -1040,13 +1040,25 @@ Recompile(JSContext *cx)
|
|||
if (!IsIonEnabled(cx))
|
||||
return true;
|
||||
|
||||
MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing);
|
||||
MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing, force);
|
||||
if (status == Method_Error)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ForcedRecompile(JSContext *cx)
|
||||
{
|
||||
return RecompileImpl(cx, /* force = */ true);
|
||||
}
|
||||
|
||||
bool
|
||||
Recompile(JSContext *cx)
|
||||
{
|
||||
return RecompileImpl(cx, /* force = */ false);
|
||||
}
|
||||
|
||||
bool
|
||||
SetDenseElement(JSContext *cx, HandleObject obj, int32_t index, HandleValue value,
|
||||
bool strict)
|
||||
|
|
|
@ -676,6 +676,7 @@ JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject descr,
|
|||
bool ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
|
||||
|
||||
bool Recompile(JSContext *cx);
|
||||
bool ForcedRecompile(JSContext *cx);
|
||||
JSString *RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp,
|
||||
HandleString repl);
|
||||
JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern,
|
||||
|
|
|
@ -4084,14 +4084,9 @@ Assembler::Condition
|
|||
MacroAssemblerARMCompat::testStringTruthy(bool truthy, const ValueOperand &value)
|
||||
{
|
||||
Register string = value.payloadReg();
|
||||
|
||||
size_t mask = (0xFFFFFFFF << JSString::LENGTH_SHIFT);
|
||||
ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLengthAndFlags()), ScratchRegister);
|
||||
// Bit clear into the scratch register. This is done because there is performs the operation
|
||||
// dest <- src1 & ~ src2. There is no instruction that does this without writing
|
||||
// the result somewhere, so the Scratch Register is sacrificed.
|
||||
ma_bic(Imm32(~mask), ScratchRegister, SetCond);
|
||||
return truthy ? Assembler::NonZero : Assembler::Zero;
|
||||
ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), ScratchRegister);
|
||||
ma_cmp(ScratchRegister, Imm32(0));
|
||||
return truthy ? Assembler::NotEqual : Assembler::Equal;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1489,20 +1489,10 @@ Simulator::setCallResult(int64_t res)
|
|||
int
|
||||
Simulator::readW(int32_t addr, SimInstruction *instr)
|
||||
{
|
||||
#ifdef JS_YARR
|
||||
// YARR emits unaligned loads, so we don't check for them here like the
|
||||
// other methods below.
|
||||
// The regexp engines emit unaligned loads, so we don't check for them here
|
||||
// like the other methods below.
|
||||
intptr_t *ptr = reinterpret_cast<intptr_t*>(addr);
|
||||
return *ptr;
|
||||
#else // JS_YARR
|
||||
if ((addr & 3) == 0) {
|
||||
intptr_t *ptr = reinterpret_cast<intptr_t*>(addr);
|
||||
return *ptr;
|
||||
} else {
|
||||
printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
#endif // JS_YARR
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1192,10 +1192,8 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||
}
|
||||
Condition testStringTruthy(bool truthy, const ValueOperand &value) {
|
||||
unboxString(value, ScratchReg);
|
||||
|
||||
Operand lengthAndFlags(ScratchReg, JSString::offsetOfLengthAndFlags());
|
||||
testq(lengthAndFlags, Imm32(-1 << JSString::LENGTH_SHIFT));
|
||||
return truthy ? Assembler::NonZero : Assembler::Zero;
|
||||
cmpl(Operand(ScratchReg, JSString::offsetOfLength()), Imm32(0));
|
||||
return truthy ? Assembler::NotEqual : Assembler::Equal;
|
||||
}
|
||||
void branchTestStringTruthy(bool truthy, const ValueOperand &value, Label *label) {
|
||||
Condition cond = testStringTruthy(truthy, value);
|
||||
|
|
|
@ -953,11 +953,8 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
}
|
||||
Condition testStringTruthy(bool truthy, const ValueOperand &value) {
|
||||
Register string = value.payloadReg();
|
||||
Operand lengthAndFlags(string, JSString::offsetOfLengthAndFlags());
|
||||
|
||||
size_t mask = (0xFFFFFFFF << JSString::LENGTH_SHIFT);
|
||||
testl(lengthAndFlags, Imm32(mask));
|
||||
return truthy ? Assembler::NonZero : Assembler::Zero;
|
||||
cmpl(Operand(string, JSString::offsetOfLength()), Imm32(0));
|
||||
return truthy ? Assembler::NotEqual : Assembler::Equal;
|
||||
}
|
||||
void branchTestStringTruthy(bool truthy, const ValueOperand &value, Label *label) {
|
||||
Condition cond = testStringTruthy(truthy, value);
|
||||
|
|
|
@ -72,6 +72,7 @@ UNIFIED_SOURCES += [
|
|||
'testTypedArrays.cpp',
|
||||
'testUncaughtError.cpp',
|
||||
'testUTF8.cpp',
|
||||
'testWeakMap.cpp',
|
||||
'testXDR.cpp',
|
||||
]
|
||||
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "gc/Zone.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
#ifdef JSGC_USE_EXACT_ROOTING
|
||||
|
||||
BEGIN_TEST(testWeakMap_basicOperations)
|
||||
{
|
||||
RootedObject map(cx, NewWeakMapObject(cx));
|
||||
CHECK(IsWeakMapObject(map));
|
||||
|
||||
RootedObject key(cx, newKey());
|
||||
CHECK(key);
|
||||
CHECK(!IsWeakMapObject(key));
|
||||
|
||||
RootedValue r(cx);
|
||||
CHECK(GetWeakMapEntry(cx, map, key, &r));
|
||||
CHECK(r.isUndefined());
|
||||
|
||||
CHECK(checkSize(map, 0));
|
||||
|
||||
RootedValue val(cx, Int32Value(1));
|
||||
CHECK(SetWeakMapEntry(cx, map, key, val));
|
||||
|
||||
CHECK(GetWeakMapEntry(cx, map, key, &r));
|
||||
CHECK(r == val);
|
||||
CHECK(checkSize(map, 1));
|
||||
|
||||
JS_GC(rt);
|
||||
|
||||
CHECK(GetWeakMapEntry(cx, map, key, &r));
|
||||
CHECK(r == val);
|
||||
CHECK(checkSize(map, 1));
|
||||
|
||||
key = nullptr;
|
||||
JS_GC(rt);
|
||||
|
||||
CHECK(checkSize(map, 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *newKey()
|
||||
{
|
||||
return JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
|
||||
}
|
||||
|
||||
bool
|
||||
checkSize(HandleObject map, uint32_t expected)
|
||||
{
|
||||
RootedObject keys(cx);
|
||||
CHECK(JS_NondeterministicGetWeakMapKeys(cx, map, &keys));
|
||||
|
||||
uint32_t length;
|
||||
CHECK(JS_GetArrayLength(cx, keys, &length));
|
||||
CHECK(length == expected);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testWeakMap_basicOperations)
|
||||
|
||||
BEGIN_TEST(testWeakMap_keyDelegates)
|
||||
{
|
||||
JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
|
||||
JS_GC(rt);
|
||||
|
||||
RootedObject map(cx, NewWeakMapObject(cx));
|
||||
CHECK(map);
|
||||
|
||||
RootedObject key(cx, newKey());
|
||||
CHECK(key);
|
||||
|
||||
RootedObject delegate(cx, newDelegate());
|
||||
CHECK(delegate);
|
||||
|
||||
SetKeyDelegate(key, delegate);
|
||||
|
||||
/*
|
||||
* Perform an incremental GC, introducing an unmarked CCW to force the map
|
||||
* zone to finish marking before the delegate zone.
|
||||
*/
|
||||
CHECK(newCCW(map, delegate));
|
||||
GCDebugSlice(rt, true, 1000000);
|
||||
#ifdef DEBUG
|
||||
CHECK(map->zone()->lastZoneGroupIndex() < delegate->zone()->lastZoneGroupIndex());
|
||||
#endif
|
||||
|
||||
/* Add our entry to the weakmap. */
|
||||
RootedValue val(cx, Int32Value(1));
|
||||
CHECK(SetWeakMapEntry(cx, map, key, val));
|
||||
CHECK(checkSize(map, 1));
|
||||
|
||||
/* Check the delegate keeps the entry alive even if the key is not reachable. */
|
||||
key = nullptr;
|
||||
CHECK(newCCW(map, delegate));
|
||||
GCDebugSlice(rt, true, 100000);
|
||||
CHECK(checkSize(map, 1));
|
||||
|
||||
/*
|
||||
* Check that the zones finished marking at the same time, which is
|
||||
* neccessary because of the presence of the delegate and the CCW.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
CHECK(map->zone()->lastZoneGroupIndex() == delegate->zone()->lastZoneGroupIndex());
|
||||
#endif
|
||||
|
||||
/* Check that when the delegate becomes unreacable the entry is removed. */
|
||||
delegate = nullptr;
|
||||
JS_GC(rt);
|
||||
CHECK(checkSize(map, 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SetKeyDelegate(JSObject *key, JSObject *delegate)
|
||||
{
|
||||
JS_SetPrivate(key, delegate);
|
||||
}
|
||||
|
||||
static JSObject *GetKeyDelegate(JSObject *obj)
|
||||
{
|
||||
return static_cast<JSObject*>(JS_GetPrivate(obj));
|
||||
}
|
||||
|
||||
JSObject *newKey()
|
||||
{
|
||||
static const js::Class keyClass = {
|
||||
"keyWithDelgate",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
|
||||
JS_PropertyStub, /* addProperty */
|
||||
JS_DeletePropertyStub, /* delProperty */
|
||||
JS_PropertyStub, /* getProperty */
|
||||
JS_StrictPropertyStub, /* setProperty */
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
{
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
GetKeyDelegate
|
||||
},
|
||||
JS_NULL_OBJECT_OPS
|
||||
};
|
||||
|
||||
RootedObject key(cx);
|
||||
key = JS_NewObject(cx,
|
||||
reinterpret_cast<const JSClass *>(&keyClass),
|
||||
JS::NullPtr(),
|
||||
JS::NullPtr());
|
||||
if (!key)
|
||||
return nullptr;
|
||||
|
||||
SetKeyDelegate(key, nullptr);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
JSObject *newCCW(HandleObject sourceZone, HandleObject destZone)
|
||||
{
|
||||
/*
|
||||
* Now ensure that this zone will be swept first by adding a cross
|
||||
* compartment wrapper to a new objct in the same zone as the
|
||||
* delegate obejct.
|
||||
*/
|
||||
RootedObject object(cx);
|
||||
{
|
||||
JSAutoCompartment ac(cx, destZone);
|
||||
object = JS_NewObject(cx, nullptr, NullPtr(), NullPtr());
|
||||
if (!object)
|
||||
return nullptr;
|
||||
}
|
||||
{
|
||||
JSAutoCompartment ac(cx, sourceZone);
|
||||
if (!JS_WrapObject(cx, &object))
|
||||
return nullptr;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
JSObject *newDelegate()
|
||||
{
|
||||
static const JSClass delegateClass = {
|
||||
"delegate",
|
||||
JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
|
||||
JS_PropertyStub,
|
||||
JS_DeletePropertyStub,
|
||||
JS_PropertyStub,
|
||||
JS_StrictPropertyStub,
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
JS_GlobalObjectTraceHook
|
||||
};
|
||||
|
||||
/* Create the global object. */
|
||||
JS::CompartmentOptions options;
|
||||
options.setVersion(JSVERSION_LATEST);
|
||||
JS::RootedObject global(cx);
|
||||
global = JS_NewGlobalObject(cx, &delegateClass, nullptr, JS::FireOnNewGlobalHook, options);
|
||||
JS_SetReservedSlot(global, 0, Int32Value(42));
|
||||
|
||||
/*
|
||||
* Ensure the delegate is not in the nursery because for the purpose of this
|
||||
* test we're going to put it in a private slot where it won't get updated.
|
||||
*/
|
||||
JS_GC(rt);
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
bool
|
||||
checkSize(HandleObject map, uint32_t expected)
|
||||
{
|
||||
RootedObject keys(cx);
|
||||
CHECK(JS_NondeterministicGetWeakMapKeys(cx, map, &keys));
|
||||
|
||||
uint32_t length;
|
||||
CHECK(JS_GetArrayLength(cx, keys, &length));
|
||||
CHECK(length == expected);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testWeakMap_keyDelegates)
|
||||
|
||||
#endif // JSGC_USE_EXACT_ROOTING
|
|
@ -586,9 +586,13 @@ struct Function {
|
|||
};
|
||||
|
||||
struct Atom {
|
||||
static const size_t LENGTH_SHIFT = 4;
|
||||
size_t lengthAndFlags;
|
||||
const jschar *chars;
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
|
||||
uint32_t flags;
|
||||
uint32_t length;
|
||||
union {
|
||||
const jschar *nonInlineChars;
|
||||
char inlineStorage[1];
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace shadow */
|
||||
|
@ -767,14 +771,19 @@ GetObjectSlot(JSObject *obj, size_t slot)
|
|||
inline const jschar *
|
||||
GetAtomChars(JSAtom *atom)
|
||||
{
|
||||
return reinterpret_cast<shadow::Atom *>(atom)->chars;
|
||||
using shadow::Atom;
|
||||
Atom *atom_ = reinterpret_cast<Atom *>(atom);
|
||||
if (atom_->flags & Atom::INLINE_CHARS_BIT) {
|
||||
char *p = reinterpret_cast<char *>(atom);
|
||||
return reinterpret_cast<const jschar *>(p + offsetof(Atom, inlineStorage));
|
||||
}
|
||||
return atom_->nonInlineChars;
|
||||
}
|
||||
|
||||
inline size_t
|
||||
GetAtomLength(JSAtom *atom)
|
||||
{
|
||||
using shadow::Atom;
|
||||
return reinterpret_cast<Atom*>(atom)->lengthAndFlags >> Atom::LENGTH_SHIFT;
|
||||
return reinterpret_cast<shadow::Atom*>(atom)->length;
|
||||
}
|
||||
|
||||
inline JSLinearString *
|
||||
|
|
|
@ -2972,8 +2972,8 @@ GCRuntime::beginMarkPhase()
|
|||
}
|
||||
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
/* Reset weak map list for the compartments being collected. */
|
||||
WeakMapBase::resetCompartmentWeakMapList(c);
|
||||
/* Unmark all weak maps in the compartments being collected. */
|
||||
WeakMapBase::unmarkCompartment(c);
|
||||
}
|
||||
|
||||
if (isFull)
|
||||
|
@ -3173,14 +3173,17 @@ js::gc::MarkingValidator::nonIncrementalMark()
|
|||
}
|
||||
|
||||
/*
|
||||
* Temporarily clear the lists of live weakmaps and array buffers for the
|
||||
* compartments we are collecting.
|
||||
* Temporarily clear the weakmaps' mark flags and the lists of live array
|
||||
* buffers for the compartments we are collecting.
|
||||
*/
|
||||
|
||||
WeakMapVector weakmaps;
|
||||
WeakMapSet markedWeakMaps;
|
||||
if (!markedWeakMaps.init())
|
||||
return;
|
||||
|
||||
ArrayBufferVector arrayBuffers;
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
|
||||
if (!WeakMapBase::saveCompartmentWeakMapList(c, weakmaps) ||
|
||||
if (!WeakMapBase::saveCompartmentMarkedWeakMaps(c, markedWeakMaps) ||
|
||||
!ArrayBufferObject::saveArrayBufferList(c, arrayBuffers))
|
||||
{
|
||||
return;
|
||||
|
@ -3194,7 +3197,7 @@ js::gc::MarkingValidator::nonIncrementalMark()
|
|||
initialized = true;
|
||||
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
|
||||
WeakMapBase::resetCompartmentWeakMapList(c);
|
||||
WeakMapBase::unmarkCompartment(c);
|
||||
ArrayBufferObject::resetArrayBufferList(c);
|
||||
}
|
||||
|
||||
|
@ -3250,10 +3253,10 @@ js::gc::MarkingValidator::nonIncrementalMark()
|
|||
}
|
||||
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
|
||||
WeakMapBase::resetCompartmentWeakMapList(c);
|
||||
WeakMapBase::unmarkCompartment(c);
|
||||
ArrayBufferObject::resetArrayBufferList(c);
|
||||
}
|
||||
WeakMapBase::restoreCompartmentWeakMapLists(weakmaps);
|
||||
WeakMapBase::restoreCompartmentMarkedWeakMaps(markedWeakMaps);
|
||||
ArrayBufferObject::restoreArrayBufferLists(arrayBuffers);
|
||||
|
||||
gc->incrementalState = state;
|
||||
|
@ -3439,13 +3442,38 @@ Zone::findOutgoingEdges(ComponentFinder<JS::Zone> &finder)
|
|||
|
||||
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next())
|
||||
comp->findOutgoingEdges(finder);
|
||||
|
||||
for (ZoneSet::Range r = gcZoneGroupEdges.all(); !r.empty(); r.popFront())
|
||||
finder.addEdgeTo(r.front());
|
||||
gcZoneGroupEdges.clear();
|
||||
}
|
||||
|
||||
bool
|
||||
GCRuntime::findZoneEdgesForWeakMaps()
|
||||
{
|
||||
/*
|
||||
* Weakmaps which have keys with delegates in a different zone introduce the
|
||||
* need for zone edges from the delegate's zone to the weakmap zone.
|
||||
*
|
||||
* Since the edges point into and not away from the zone the weakmap is in
|
||||
* we must find these edges in advance and store them in a set on the Zone.
|
||||
* If we run out of memory, we fall back to sweeping everything in one
|
||||
* group.
|
||||
*/
|
||||
|
||||
for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) {
|
||||
if (!WeakMapBase::findZoneEdgesForCompartment(comp))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::findZoneGroups()
|
||||
{
|
||||
ComponentFinder<Zone> finder(rt->mainThread.nativeStackLimit[StackForSystemCode]);
|
||||
if (!isIncremental)
|
||||
if (!isIncremental || !findZoneEdgesForWeakMaps())
|
||||
finder.useOneComponent();
|
||||
|
||||
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||
|
@ -3787,6 +3815,8 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
|
||||
if (rt->sweepZoneCallback)
|
||||
rt->sweepZoneCallback(zone);
|
||||
|
||||
zone->gcLastZoneGroupIndex = zoneGroupIndex;
|
||||
}
|
||||
|
||||
validateIncrementalMarking();
|
||||
|
@ -5019,6 +5049,9 @@ js::NewCompartment(JSContext *cx, Zone *zone, JSPrincipals *principals,
|
|||
|
||||
zoneHolder.reset(zone);
|
||||
|
||||
if (!zone->init())
|
||||
return nullptr;
|
||||
|
||||
zone->setGCLastBytes(8192, GC_NORMAL);
|
||||
|
||||
const JSPrincipals *trusted = rt->trustedPrincipals();
|
||||
|
|
|
@ -20,18 +20,52 @@
|
|||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
WeakMapBase::WeakMapBase(JSObject *memOf, JSCompartment *c)
|
||||
: memberOf(memOf),
|
||||
compartment(c),
|
||||
next(WeakMapNotInList)
|
||||
next(WeakMapNotInList),
|
||||
marked(false)
|
||||
{
|
||||
JS_ASSERT_IF(memberOf, memberOf->compartment() == c);
|
||||
}
|
||||
|
||||
WeakMapBase::~WeakMapBase()
|
||||
{
|
||||
JS_ASSERT(next == WeakMapNotInList);
|
||||
JS_ASSERT(!isInList());
|
||||
}
|
||||
|
||||
void
|
||||
WeakMapBase::trace(JSTracer *tracer)
|
||||
{
|
||||
JS_ASSERT(isInList());
|
||||
if (IS_GC_MARKING_TRACER(tracer)) {
|
||||
// We don't trace any of the WeakMap entries at this time, just record
|
||||
// record the fact that the WeakMap has been marked. Enties are marked
|
||||
// in the iterative marking phase by markAllIteratively(), which happens
|
||||
// when many keys as possible have been marked already.
|
||||
JS_ASSERT(tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps);
|
||||
marked = true;
|
||||
} else {
|
||||
// If we're not actually doing garbage collection, the keys won't be marked
|
||||
// nicely as needed by the true ephemeral marking algorithm --- custom tracers
|
||||
// such as the cycle collector must use their own means for cycle detection.
|
||||
// So here we do a conservative approximation: pretend all keys are live.
|
||||
if (tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps)
|
||||
return;
|
||||
|
||||
nonMarkingTraceValues(tracer);
|
||||
if (tracer->eagerlyTraceWeakMaps() == TraceWeakMapKeysValues)
|
||||
nonMarkingTraceKeys(tracer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WeakMapBase::unmarkCompartment(JSCompartment *c)
|
||||
{
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
|
||||
m->marked = false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -39,17 +73,44 @@ WeakMapBase::markCompartmentIteratively(JSCompartment *c, JSTracer *tracer)
|
|||
{
|
||||
bool markedAny = false;
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
|
||||
if (m->markIteratively(tracer))
|
||||
if (m->marked && m->markIteratively(tracer))
|
||||
markedAny = true;
|
||||
}
|
||||
return markedAny;
|
||||
}
|
||||
|
||||
bool
|
||||
WeakMapBase::findZoneEdgesForCompartment(JSCompartment *c)
|
||||
{
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
|
||||
if (!m->findZoneEdges())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WeakMapBase::sweepCompartment(JSCompartment *c)
|
||||
{
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
|
||||
WeakMapBase **tailPtr = &c->gcWeakMapList;
|
||||
for (WeakMapBase *m = c->gcWeakMapList, *next; m; m = next) {
|
||||
next = m->next;
|
||||
if (m->marked) {
|
||||
m->sweep();
|
||||
*tailPtr = m;
|
||||
tailPtr = &m->next;
|
||||
} else {
|
||||
/* Destroy the hash map now to catch any use after this point. */
|
||||
m->finish();
|
||||
m->next = WeakMapNotInList;
|
||||
}
|
||||
}
|
||||
*tailPtr = nullptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
|
||||
JS_ASSERT(m->isInList() && m->marked);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -62,41 +123,24 @@ WeakMapBase::traceAllMappings(WeakMapTracer *tracer)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
WeakMapBase::resetCompartmentWeakMapList(JSCompartment *c)
|
||||
{
|
||||
JS_ASSERT(WeakMapNotInList != nullptr);
|
||||
|
||||
WeakMapBase *m = c->gcWeakMapList;
|
||||
c->gcWeakMapList = nullptr;
|
||||
while (m) {
|
||||
WeakMapBase *n = m->next;
|
||||
m->next = WeakMapNotInList;
|
||||
m = n;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WeakMapBase::saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector)
|
||||
WeakMapBase::saveCompartmentMarkedWeakMaps(JSCompartment *c, WeakMapSet &markedWeakMaps)
|
||||
{
|
||||
WeakMapBase *m = c->gcWeakMapList;
|
||||
while (m) {
|
||||
if (!vector.append(m))
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
|
||||
if (m->marked && !markedWeakMaps.put(m))
|
||||
return false;
|
||||
m = m->next;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WeakMapBase::restoreCompartmentWeakMapLists(WeakMapVector &vector)
|
||||
WeakMapBase::restoreCompartmentMarkedWeakMaps(WeakMapSet &markedWeakMaps)
|
||||
{
|
||||
for (WeakMapBase **p = vector.begin(); p != vector.end(); p++) {
|
||||
WeakMapBase *m = *p;
|
||||
JS_ASSERT(m->next == WeakMapNotInList);
|
||||
JSCompartment *c = m->compartment;
|
||||
m->next = c->gcWeakMapList;
|
||||
c->gcWeakMapList = m;
|
||||
for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
|
||||
WeakMapBase *map = r.front();
|
||||
JS_ASSERT(map->compartment->zone()->isGCMarking());
|
||||
JS_ASSERT(!map->marked);
|
||||
map->marked = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +157,35 @@ WeakMapBase::removeWeakMapFromList(WeakMapBase *weakmap)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ObjectValueMap::findZoneEdges()
|
||||
{
|
||||
/*
|
||||
* For unmarked weakmap keys with delegates in a different zone, add a zone
|
||||
* edge to ensure that the delegate zone does finish marking after the key
|
||||
* zone.
|
||||
*/
|
||||
JS::AutoAssertNoGC nogc;
|
||||
Zone *mapZone = compartment->zone();
|
||||
for (Range r = all(); !r.empty(); r.popFront()) {
|
||||
JSObject *key = r.front().key();
|
||||
if (key->isMarked(BLACK) && !key->isMarked(GRAY))
|
||||
continue;
|
||||
JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp;
|
||||
if (!op)
|
||||
continue;
|
||||
JSObject *delegate = op(key);
|
||||
if (!delegate)
|
||||
continue;
|
||||
Zone *delegateZone = delegate->zone();
|
||||
if (delegateZone == mapZone)
|
||||
continue;
|
||||
if (!delegateZone->gcZoneGroupEdges.put(key->zone()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
GetKeyArg(JSContext *cx, CallArgs &args)
|
||||
{
|
||||
|
@ -166,8 +239,8 @@ WeakMap_clear_impl(JSContext *cx, CallArgs args)
|
|||
{
|
||||
JS_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
// We can't js_delete the weakmap because the data gathered during GC
|
||||
// is used by the Cycle Collector
|
||||
// We can't js_delete the weakmap because the data gathered during GC is
|
||||
// used by the Cycle Collector.
|
||||
if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap())
|
||||
map->clear();
|
||||
|
||||
|
@ -286,7 +359,7 @@ WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *weakMap, JSObject *key)
|
|||
typedef HashMap<JSObject *, Value> UnbarrieredMap;
|
||||
UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
|
||||
|
||||
typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
|
||||
typedef HashKeyRef<UnbarrieredMap, JSObject *> Ref;
|
||||
if (key && IsInsideNursery(rt, key))
|
||||
rt->gc.storeBuffer.putGeneric(Ref((unbarrieredMap), key));
|
||||
#endif
|
||||
|
@ -373,7 +446,7 @@ JS_NondeterministicGetWeakMapKeys(JSContext *cx, HandleObject objArg, MutableHan
|
|||
ObjectValueMap *map = obj->as<WeakMapObject>().getMap();
|
||||
if (map) {
|
||||
// Prevent GC from mutating the weakmap while iterating.
|
||||
gc::AutoSuppressGC suppress(cx);
|
||||
AutoSuppressGC suppress(cx);
|
||||
for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
|
||||
RootedObject key(cx, r.front().key());
|
||||
if (!cx->compartment()->wrap(cx, &key))
|
||||
|
@ -397,7 +470,6 @@ static void
|
|||
WeakMap_finalize(FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
if (ObjectValueMap *map = obj->as<WeakMapObject>().getMap()) {
|
||||
map->check();
|
||||
#ifdef DEBUG
|
||||
map->~ObjectValueMap();
|
||||
memset(static_cast<void *>(map), 0xdc, sizeof(*map));
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace js {
|
|||
// The value for the next pointer for maps not in the map list.
|
||||
static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
|
||||
|
||||
typedef Vector<WeakMapBase *, 0, SystemAllocPolicy> WeakMapVector;
|
||||
typedef HashSet<WeakMapBase *, DefaultHasher<WeakMapBase *>, SystemAllocPolicy> WeakMapSet;
|
||||
|
||||
// Common base class for all WeakMap specializations. The collector uses this to call
|
||||
// their markIteratively and sweep methods.
|
||||
|
@ -42,62 +42,38 @@ class WeakMapBase {
|
|||
WeakMapBase(JSObject *memOf, JSCompartment *c);
|
||||
virtual ~WeakMapBase();
|
||||
|
||||
void trace(JSTracer *tracer) {
|
||||
if (IS_GC_MARKING_TRACER(tracer)) {
|
||||
// We don't do anything with a WeakMap at trace time. Rather, we wait until as
|
||||
// many keys as possible have been marked, and add ourselves to the list of
|
||||
// known-live WeakMaps to be scanned in the iterative marking phase, by
|
||||
// markAllIteratively.
|
||||
JS_ASSERT(tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps);
|
||||
|
||||
// Add ourselves to the list if we are not already in the list. We can already
|
||||
// be in the list if the weak map is marked more than once due delayed marking.
|
||||
if (next == WeakMapNotInList) {
|
||||
next = compartment->gcWeakMapList;
|
||||
compartment->gcWeakMapList = this;
|
||||
}
|
||||
} else {
|
||||
// If we're not actually doing garbage collection, the keys won't be marked
|
||||
// nicely as needed by the true ephemeral marking algorithm --- custom tracers
|
||||
// such as the cycle collector must use their own means for cycle detection.
|
||||
// So here we do a conservative approximation: pretend all keys are live.
|
||||
if (tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps)
|
||||
return;
|
||||
|
||||
nonMarkingTraceValues(tracer);
|
||||
if (tracer->eagerlyTraceWeakMaps() == TraceWeakMapKeysValues)
|
||||
nonMarkingTraceKeys(tracer);
|
||||
}
|
||||
}
|
||||
void trace(JSTracer *tracer);
|
||||
|
||||
// Garbage collector entry points.
|
||||
|
||||
// Unmark all weak maps in a compartment.
|
||||
static void unmarkCompartment(JSCompartment *c);
|
||||
|
||||
// Check all weak maps in a compartment that have been marked as live in this garbage
|
||||
// collection, and mark the values of all entries that have become strong references
|
||||
// to them. Return true if we marked any new values, indicating that we need to make
|
||||
// another pass. In other words, mark my marked maps' marked members' mid-collection.
|
||||
static bool markCompartmentIteratively(JSCompartment *c, JSTracer *tracer);
|
||||
|
||||
// Remove entries whose keys are dead from all weak maps in a compartment marked as
|
||||
// live in this garbage collection.
|
||||
// Add zone edges for weakmaps with key delegates in a different zone.
|
||||
static bool findZoneEdgesForCompartment(JSCompartment *c);
|
||||
|
||||
// Sweep the weak maps in a compartment, removing dead weak maps and removing
|
||||
// entries of live weak maps whose keys are dead.
|
||||
static void sweepCompartment(JSCompartment *c);
|
||||
|
||||
// Trace all delayed weak map bindings. Used by the cycle collector.
|
||||
static void traceAllMappings(WeakMapTracer *tracer);
|
||||
|
||||
bool isInList() { return next != WeakMapNotInList; }
|
||||
void check() { JS_ASSERT(!isInList()); }
|
||||
|
||||
// Remove everything from the weak map list for a compartment.
|
||||
static void resetCompartmentWeakMapList(JSCompartment *c);
|
||||
// Save information about which weak maps are marked for a compartment.
|
||||
static bool saveCompartmentMarkedWeakMaps(JSCompartment *c, WeakMapSet &markedWeakMaps);
|
||||
|
||||
// Save the live weak map list for a compartment, appending the data to a vector.
|
||||
static bool saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector);
|
||||
// Restore information about which weak maps are marked for many compartments.
|
||||
static void restoreCompartmentMarkedWeakMaps(WeakMapSet &markedWeakMaps);
|
||||
|
||||
// Restore live weak map lists for multiple compartments from a vector.
|
||||
static void restoreCompartmentWeakMapLists(WeakMapVector &vector);
|
||||
|
||||
// Remove a weakmap from the live weakmaps list
|
||||
// Remove a weakmap from its compartment's weakmaps list.
|
||||
static void removeWeakMapFromList(WeakMapBase *weakmap);
|
||||
|
||||
protected:
|
||||
|
@ -106,8 +82,10 @@ class WeakMapBase {
|
|||
virtual void nonMarkingTraceKeys(JSTracer *tracer) = 0;
|
||||
virtual void nonMarkingTraceValues(JSTracer *tracer) = 0;
|
||||
virtual bool markIteratively(JSTracer *tracer) = 0;
|
||||
virtual bool findZoneEdges() = 0;
|
||||
virtual void sweep() = 0;
|
||||
virtual void traceMappings(WeakMapTracer *tracer) = 0;
|
||||
virtual void finish() = 0;
|
||||
|
||||
// Object that this weak map is part of, if any.
|
||||
JSObject *memberOf;
|
||||
|
@ -115,14 +93,13 @@ class WeakMapBase {
|
|||
// Compartment that this weak map is part of.
|
||||
JSCompartment *compartment;
|
||||
|
||||
private:
|
||||
// Link in a list of WeakMaps to mark iteratively and sweep in this garbage
|
||||
// collection, headed by JSCompartment::gcWeakMapList. The last element of
|
||||
// the list has nullptr as its next. Maps not in the list have
|
||||
// WeakMapNotInList as their next. We must distinguish these cases to
|
||||
// avoid creating infinite lists when a weak map gets traced twice due to
|
||||
// delayed marking.
|
||||
// Link in a list of all WeakMaps in a compartment, headed by
|
||||
// JSCompartment::gcWeakMapList. The last element of the list has nullptr as
|
||||
// its next. Maps not in the list have WeakMapNotInList as their next.
|
||||
WeakMapBase *next;
|
||||
|
||||
// Whether this object has been traced during garbage collection.
|
||||
bool marked;
|
||||
};
|
||||
|
||||
template <class Key, class Value,
|
||||
|
@ -138,6 +115,15 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
|||
explicit WeakMap(JSContext *cx, JSObject *memOf = nullptr)
|
||||
: Base(cx->runtime()), WeakMapBase(memOf, cx->compartment()) { }
|
||||
|
||||
bool init(uint32_t len = 16) {
|
||||
if (!Base::init(len))
|
||||
return false;
|
||||
next = compartment->gcWeakMapList;
|
||||
compartment->gcWeakMapList = this;
|
||||
marked = JS::IsIncrementalGCInProgress(compartment->runtimeFromMainThread());
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool markValue(JSTracer *trc, Value *x) {
|
||||
if (gc::IsMarked(x))
|
||||
|
@ -200,6 +186,11 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
|||
return markedAny;
|
||||
}
|
||||
|
||||
bool findZoneEdges() {
|
||||
// This is overridden by ObjectValueMap.
|
||||
return true;
|
||||
}
|
||||
|
||||
void sweep() {
|
||||
/* Remove all entries whose keys remain unmarked. */
|
||||
for (Enum e(*this); !e.empty(); e.popFront()) {
|
||||
|
@ -216,6 +207,10 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
|||
assertEntriesNotAboutToBeFinalized();
|
||||
}
|
||||
|
||||
void finish() {
|
||||
Base::finish();
|
||||
}
|
||||
|
||||
/* memberOf can be nullptr, which means that the map is not part of a JSObject. */
|
||||
void traceMappings(WeakMapTracer *tracer) {
|
||||
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
||||
|
|
|
@ -571,6 +571,10 @@ class ObjectImpl : public gc::BarrieredCell<ObjectImpl>
|
|||
uint32_t numFixedSlots() const {
|
||||
return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots();
|
||||
}
|
||||
uint32_t numUsedFixedSlots() const {
|
||||
uint32_t nslots = lastProperty()->slotSpan(getClass());
|
||||
return Min(nslots, numFixedSlots());
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether this is the only object which has its specified type. This
|
||||
|
|
|
@ -297,7 +297,7 @@ JSRuntime::init(uint32_t maxbytes)
|
|||
SetMarkStackLimit(this, atoi(size));
|
||||
|
||||
ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
|
||||
if (!atomsZone)
|
||||
if (!atomsZone || !atomsZone->init())
|
||||
return false;
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
|
|
|
@ -25,13 +25,22 @@ NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars)
|
|||
{
|
||||
size_t len = chars.length();
|
||||
JS_ASSERT(JSFatInlineString::lengthFits(len));
|
||||
JSInlineString *str = JSInlineString::lengthFits(len)
|
||||
? JSInlineString::new_<allowGC>(cx)
|
||||
: JSFatInlineString::new_<allowGC>(cx);
|
||||
|
||||
JSInlineString *str;
|
||||
jschar *p;
|
||||
if (JSInlineString::lengthFits(len)) {
|
||||
str = JSInlineString::new_<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
p = str->init(len);
|
||||
} else {
|
||||
JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
|
||||
if (!fatstr)
|
||||
return nullptr;
|
||||
p = fatstr->init(len);
|
||||
str = fatstr;
|
||||
}
|
||||
|
||||
jschar *p = str->init(len);
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
p[i] = static_cast<jschar>(chars[i]);
|
||||
p[len] = '\0';
|
||||
|
@ -49,13 +58,22 @@ NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars)
|
|||
* many get here (for one, Atomize is catching them).
|
||||
*/
|
||||
JS_ASSERT(JSFatInlineString::lengthFits(len));
|
||||
JSInlineString *str = JSInlineString::lengthFits(len)
|
||||
? JSInlineString::new_<allowGC>(cx)
|
||||
: JSFatInlineString::new_<allowGC>(cx);
|
||||
|
||||
JSInlineString *str;
|
||||
jschar *storage;
|
||||
if (JSInlineString::lengthFits(len)) {
|
||||
str = JSInlineString::new_<allowGC>(cx);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
storage = str->init(len);
|
||||
} else {
|
||||
JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
|
||||
if (!fatstr)
|
||||
return nullptr;
|
||||
storage = fatstr->init(len);
|
||||
str = fatstr;
|
||||
}
|
||||
|
||||
jschar *storage = str->init(len);
|
||||
mozilla::PodCopy(storage, chars.start().get(), len);
|
||||
storage[len] = 0;
|
||||
return str;
|
||||
|
@ -87,11 +105,12 @@ JSString::validateLength(js::ThreadSafeContext *maybecx, size_t length)
|
|||
MOZ_ALWAYS_INLINE void
|
||||
JSRope::init(js::ThreadSafeContext *cx, JSString *left, JSString *right, size_t length)
|
||||
{
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length, ROPE_FLAGS);
|
||||
d.u1.left = left;
|
||||
d.s.u2.right = right;
|
||||
js::StringWriteBarrierPost(cx, &d.u1.left);
|
||||
js::StringWriteBarrierPost(cx, &d.s.u2.right);
|
||||
d.u1.length = length;
|
||||
d.u1.flags = ROPE_FLAGS;
|
||||
d.s.u2.left = left;
|
||||
d.s.u3.right = right;
|
||||
js::StringWriteBarrierPost(cx, &d.s.u2.left);
|
||||
js::StringWriteBarrierPost(cx, &d.s.u3.right);
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
|
@ -113,8 +132,8 @@ JSRope::new_(js::ThreadSafeContext *cx,
|
|||
inline void
|
||||
JSRope::markChildren(JSTracer *trc)
|
||||
{
|
||||
js::gc::MarkStringUnbarriered(trc, &d.u1.left, "left child");
|
||||
js::gc::MarkStringUnbarriered(trc, &d.s.u2.right, "right child");
|
||||
js::gc::MarkStringUnbarriered(trc, &d.s.u2.left, "left child");
|
||||
js::gc::MarkStringUnbarriered(trc, &d.s.u3.right, "right child");
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
|
@ -122,10 +141,11 @@ JSDependentString::init(js::ThreadSafeContext *cx, JSLinearString *base, const j
|
|||
size_t length)
|
||||
{
|
||||
JS_ASSERT(!js::IsPoisonedPtr(base));
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_FLAGS);
|
||||
d.u1.chars = chars;
|
||||
d.s.u2.base = base;
|
||||
js::StringWriteBarrierPost(cx, reinterpret_cast<JSString **>(&d.s.u2.base));
|
||||
d.u1.length = length;
|
||||
d.u1.flags = DEPENDENT_FLAGS;
|
||||
d.s.u2.nonInlineChars = chars;
|
||||
d.s.u3.base = base;
|
||||
js::StringWriteBarrierPost(cx, reinterpret_cast<JSString **>(&d.s.u3.base));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE JSLinearString *
|
||||
|
@ -179,14 +199,15 @@ inline void
|
|||
JSString::markBase(JSTracer *trc)
|
||||
{
|
||||
JS_ASSERT(hasBase());
|
||||
js::gc::MarkStringUnbarriered(trc, &d.s.u2.base, "base");
|
||||
js::gc::MarkStringUnbarriered(trc, &d.s.u3.base, "base");
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
JSFlatString::init(const jschar *chars, size_t length)
|
||||
{
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
|
||||
d.u1.chars = chars;
|
||||
d.u1.length = length;
|
||||
d.u1.flags = FLAT_BIT;
|
||||
d.s.u2.nonInlineChars = chars;
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
|
@ -229,17 +250,19 @@ JSInlineString::new_(js::ThreadSafeContext *cx)
|
|||
MOZ_ALWAYS_INLINE jschar *
|
||||
JSInlineString::init(size_t length)
|
||||
{
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
|
||||
d.u1.chars = d.inlineStorage;
|
||||
JS_ASSERT(lengthFits(length) || (isFatInline() && JSFatInlineString::lengthFits(length)));
|
||||
JS_ASSERT(lengthFits(length));
|
||||
d.u1.length = length;
|
||||
d.u1.flags = INIT_INLINE_FLAGS;
|
||||
return d.inlineStorage;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
JSInlineString::resetLength(size_t length)
|
||||
MOZ_ALWAYS_INLINE jschar *
|
||||
JSFatInlineString::init(size_t length)
|
||||
{
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
|
||||
JS_ASSERT(lengthFits(length) || (isFatInline() && JSFatInlineString::lengthFits(length)));
|
||||
JS_ASSERT(lengthFits(length));
|
||||
d.u1.length = length;
|
||||
d.u1.flags = INIT_FAT_INLINE_FLAGS;
|
||||
return d.inlineStorage;
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
|
@ -254,9 +277,10 @@ JSExternalString::init(const jschar *chars, size_t length, const JSStringFinaliz
|
|||
{
|
||||
JS_ASSERT(fin);
|
||||
JS_ASSERT(fin->finalize);
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
|
||||
d.u1.chars = chars;
|
||||
d.s.u2.externalFinalizer = fin;
|
||||
d.u1.length = length;
|
||||
d.u1.flags = EXTERNAL_FLAGS;
|
||||
d.s.u2.nonInlineChars = chars;
|
||||
d.s.u3.externalFinalizer = fin;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE JSExternalString *
|
||||
|
@ -314,8 +338,8 @@ JSFlatString::finalize(js::FreeOp *fop)
|
|||
{
|
||||
JS_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING);
|
||||
|
||||
if (chars() != d.inlineStorage)
|
||||
fop->free_(const_cast<jschar *>(chars()));
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -323,8 +347,8 @@ JSFatInlineString::finalize(js::FreeOp *fop)
|
|||
{
|
||||
JS_ASSERT(getAllocKind() == js::gc::FINALIZE_FAT_INLINE_STRING);
|
||||
|
||||
if (chars() != d.inlineStorage)
|
||||
fop->free_(const_cast<jschar *>(chars()));
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -333,15 +357,15 @@ JSAtom::finalize(js::FreeOp *fop)
|
|||
JS_ASSERT(JSString::isAtom());
|
||||
JS_ASSERT(JSString::isFlat());
|
||||
|
||||
if (chars() != d.inlineStorage)
|
||||
fop->free_(const_cast<jschar *>(chars()));
|
||||
if (!isInline())
|
||||
fop->free_(const_cast<jschar *>(nonInlineChars()));
|
||||
}
|
||||
|
||||
inline void
|
||||
JSExternalString::finalize(js::FreeOp *fop)
|
||||
{
|
||||
const JSStringFinalizer *fin = externalFinalizer();
|
||||
fin->finalize(fin, const_cast<jschar *>(chars()));
|
||||
fin->finalize(fin, const_cast<jschar *>(nonInlineChars()));
|
||||
}
|
||||
|
||||
#endif /* vm_String_inl_h */
|
||||
|
|
|
@ -22,25 +22,6 @@ using mozilla::PodCopy;
|
|||
using mozilla::RangedPtr;
|
||||
using mozilla::RoundUpPow2;
|
||||
|
||||
bool
|
||||
JSString::isFatInline() const
|
||||
{
|
||||
// It's possible for fat-inline strings to be converted to flat strings;
|
||||
// as a result, checking just for the arena isn't enough to determine if a
|
||||
// string is fat-inline. Hence the isInline() check.
|
||||
bool is_FatInline = (getAllocKind() == gc::FINALIZE_FAT_INLINE_STRING) && isInline();
|
||||
JS_ASSERT_IF(is_FatInline, isFlat());
|
||||
return is_FatInline;
|
||||
}
|
||||
|
||||
bool
|
||||
JSString::isExternal() const
|
||||
{
|
||||
bool is_external = (getAllocKind() == gc::FINALIZE_EXTERNAL_STRING);
|
||||
JS_ASSERT_IF(is_external, isFlat());
|
||||
return is_external;
|
||||
}
|
||||
|
||||
size_t
|
||||
JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
|
@ -59,7 +40,7 @@ JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
|||
// JSExtensibleString: count the full capacity, not just the used space.
|
||||
if (isExtensible()) {
|
||||
JSExtensibleString &extensible = asExtensible();
|
||||
return mallocSizeOf(extensible.chars());
|
||||
return mallocSizeOf(extensible.nonInlineChars());
|
||||
}
|
||||
|
||||
// JSExternalString: don't count, the chars could be stored anywhere.
|
||||
|
@ -283,29 +264,28 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
JS_ASSERT(str->isRope());
|
||||
while (str != leftMostRope) {
|
||||
if (b == WithIncrementalBarrier) {
|
||||
JSString::writeBarrierPre(str->d.u1.left);
|
||||
JSString::writeBarrierPre(str->d.s.u2.right);
|
||||
JSString::writeBarrierPre(str->d.s.u2.left);
|
||||
JSString::writeBarrierPre(str->d.s.u3.right);
|
||||
}
|
||||
JSString *child = str->d.u1.left;
|
||||
JSString *child = str->d.s.u2.left;
|
||||
JS_ASSERT(child->isRope());
|
||||
str->d.u1.chars = left.chars();
|
||||
child->d.u0.flattenData = uintptr_t(str) | Tag_VisitRightChild;
|
||||
str->d.s.u2.nonInlineChars = left.nonInlineChars();
|
||||
child->d.u1.flattenData = uintptr_t(str) | Tag_VisitRightChild;
|
||||
str = child;
|
||||
}
|
||||
if (b == WithIncrementalBarrier) {
|
||||
JSString::writeBarrierPre(str->d.u1.left);
|
||||
JSString::writeBarrierPre(str->d.s.u2.right);
|
||||
JSString::writeBarrierPre(str->d.s.u2.left);
|
||||
JSString::writeBarrierPre(str->d.s.u3.right);
|
||||
}
|
||||
str->d.u1.chars = left.chars();
|
||||
str->d.s.u2.nonInlineChars = left.nonInlineChars();
|
||||
wholeCapacity = capacity;
|
||||
wholeChars = const_cast<jschar *>(left.chars());
|
||||
size_t bits = left.d.u0.lengthAndFlags;
|
||||
pos = wholeChars + (bits >> LENGTH_SHIFT);
|
||||
wholeChars = const_cast<jschar *>(left.nonInlineChars());
|
||||
pos = wholeChars + left.d.u1.length;
|
||||
JS_STATIC_ASSERT(!(EXTENSIBLE_FLAGS & DEPENDENT_FLAGS));
|
||||
left.d.u0.lengthAndFlags = bits ^ (EXTENSIBLE_FLAGS | DEPENDENT_FLAGS);
|
||||
left.d.s.u2.base = (JSLinearString *)this; /* will be true on exit */
|
||||
StringWriteBarrierPostRemove(maybecx, &left.d.u1.left);
|
||||
StringWriteBarrierPost(maybecx, (JSString **)&left.d.s.u2.base);
|
||||
left.d.u1.flags ^= (EXTENSIBLE_FLAGS | DEPENDENT_FLAGS);
|
||||
left.d.s.u3.base = (JSLinearString *)this; /* will be true on exit */
|
||||
StringWriteBarrierPostRemove(maybecx, &left.d.s.u2.left);
|
||||
StringWriteBarrierPost(maybecx, (JSString **)&left.d.s.u3.base);
|
||||
goto visit_right_child;
|
||||
}
|
||||
}
|
||||
|
@ -316,50 +296,52 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
pos = wholeChars;
|
||||
first_visit_node: {
|
||||
if (b == WithIncrementalBarrier) {
|
||||
JSString::writeBarrierPre(str->d.u1.left);
|
||||
JSString::writeBarrierPre(str->d.s.u2.right);
|
||||
JSString::writeBarrierPre(str->d.s.u2.left);
|
||||
JSString::writeBarrierPre(str->d.s.u3.right);
|
||||
}
|
||||
|
||||
JSString &left = *str->d.u1.left;
|
||||
str->d.u1.chars = pos;
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.u1.left);
|
||||
JSString &left = *str->d.s.u2.left;
|
||||
str->d.s.u2.nonInlineChars = pos;
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
|
||||
if (left.isRope()) {
|
||||
/* Return to this node when 'left' done, then goto visit_right_child. */
|
||||
left.d.u0.flattenData = uintptr_t(str) | Tag_VisitRightChild;
|
||||
left.d.u1.flattenData = uintptr_t(str) | Tag_VisitRightChild;
|
||||
str = &left;
|
||||
goto first_visit_node;
|
||||
}
|
||||
size_t len = left.length();
|
||||
PodCopy(pos, left.d.u1.chars, len);
|
||||
PodCopy(pos, left.asLinear().chars(), len);
|
||||
pos += len;
|
||||
}
|
||||
visit_right_child: {
|
||||
JSString &right = *str->d.s.u2.right;
|
||||
JSString &right = *str->d.s.u3.right;
|
||||
if (right.isRope()) {
|
||||
/* Return to this node when 'right' done, then goto finish_node. */
|
||||
right.d.u0.flattenData = uintptr_t(str) | Tag_FinishNode;
|
||||
right.d.u1.flattenData = uintptr_t(str) | Tag_FinishNode;
|
||||
str = &right;
|
||||
goto first_visit_node;
|
||||
}
|
||||
size_t len = right.length();
|
||||
PodCopy(pos, right.d.u1.chars, len);
|
||||
PodCopy(pos, right.asLinear().chars(), len);
|
||||
pos += len;
|
||||
}
|
||||
finish_node: {
|
||||
if (str == this) {
|
||||
JS_ASSERT(pos == wholeChars + wholeLength);
|
||||
*pos = '\0';
|
||||
str->d.u0.lengthAndFlags = buildLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS);
|
||||
str->d.u1.chars = wholeChars;
|
||||
str->d.s.u2.capacity = wholeCapacity;
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.u1.left);
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.right);
|
||||
str->d.u1.length = wholeLength;
|
||||
str->d.u1.flags = EXTENSIBLE_FLAGS;
|
||||
str->d.s.u2.nonInlineChars = wholeChars;
|
||||
str->d.s.u3.capacity = wholeCapacity;
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
|
||||
StringWriteBarrierPostRemove(maybecx, &str->d.s.u3.right);
|
||||
return &this->asFlat();
|
||||
}
|
||||
uintptr_t flattenData = str->d.u0.flattenData;
|
||||
str->d.u0.lengthAndFlags = buildLengthAndFlags(pos - str->d.u1.chars, DEPENDENT_FLAGS);
|
||||
str->d.s.u2.base = (JSLinearString *)this; /* will be true on exit */
|
||||
StringWriteBarrierPost(maybecx, (JSString **)&str->d.s.u2.base);
|
||||
uintptr_t flattenData = str->d.u1.flattenData;
|
||||
str->d.u1.flags = DEPENDENT_FLAGS;
|
||||
str->d.u1.length = pos - str->d.s.u2.nonInlineChars;
|
||||
str->d.s.u3.base = (JSLinearString *)this; /* will be true on exit */
|
||||
StringWriteBarrierPost(maybecx, (JSString **)&str->d.s.u3.base);
|
||||
str = (JSString *)(flattenData & ~Tag_Mask);
|
||||
if ((flattenData & Tag_Mask) == Tag_VisitRightChild)
|
||||
goto visit_right_child;
|
||||
|
@ -439,7 +421,7 @@ JSDependentString::copyNonPureCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jsch
|
|||
if (!s)
|
||||
return false;
|
||||
|
||||
PodCopy(s, chars(), n);
|
||||
PodCopy(s, nonInlineChars(), n);
|
||||
s[n] = 0;
|
||||
|
||||
out.reset(s);
|
||||
|
@ -464,15 +446,15 @@ JSDependentString::undepend(ExclusiveContext *cx)
|
|||
if (!s)
|
||||
return nullptr;
|
||||
|
||||
PodCopy(s, chars(), n);
|
||||
PodCopy(s, nonInlineChars(), n);
|
||||
s[n] = 0;
|
||||
d.u1.chars = s;
|
||||
d.s.u2.nonInlineChars = s;
|
||||
|
||||
/*
|
||||
* Transform *this into an undepended string so 'base' will remain rooted
|
||||
* for the benefit of any other dependent string that depends on *this.
|
||||
*/
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(n, UNDEPENDED_FLAGS);
|
||||
d.u1.flags = UNDEPENDED_FLAGS;
|
||||
|
||||
return &this->asFlat();
|
||||
}
|
||||
|
|
|
@ -137,22 +137,25 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
struct Data
|
||||
{
|
||||
union {
|
||||
size_t lengthAndFlags; /* JSString */
|
||||
struct {
|
||||
uint32_t flags; /* JSString */
|
||||
uint32_t length; /* JSString */
|
||||
};
|
||||
uintptr_t flattenData; /* JSRope (temporary while flattening) */
|
||||
} u0;
|
||||
union {
|
||||
const jschar *chars; /* JSLinearString */
|
||||
JSString *left; /* JSRope */
|
||||
} u1;
|
||||
union {
|
||||
jschar inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|FatInline)String */
|
||||
struct {
|
||||
union {
|
||||
const jschar *nonInlineChars; /* JSLinearString, except JS(Inline|FatInline)String */
|
||||
JSString *left; /* JSRope */
|
||||
} u2;
|
||||
union {
|
||||
JSLinearString *base; /* JS(Dependent|Undepended)String */
|
||||
JSString *right; /* JSRope */
|
||||
size_t capacity; /* JSFlatString (extensible) */
|
||||
const JSStringFinalizer *externalFinalizer;/* JSExternalString */
|
||||
} u2;
|
||||
} u3;
|
||||
} s;
|
||||
};
|
||||
} d;
|
||||
|
@ -161,10 +164,6 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
/* Flags exposed only for jits */
|
||||
|
||||
/*
|
||||
* The low LENGTH_SHIFT bits of lengthAndFlags are used to encode the type
|
||||
* of the string. The remaining bits store the string length (which must be
|
||||
* less or equal than MAX_LENGTH).
|
||||
*
|
||||
* Instead of using a dense index to represent the most-derived type, string
|
||||
* types are encoded to allow single-op tests for hot queries (isRope,
|
||||
* isDependent, isFlat, isAtom) which, in view of subtyping, would require
|
||||
|
@ -177,20 +176,31 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
* the predicate used to query whether a JSString instance is subtype
|
||||
* (reflexively) of that type.
|
||||
*
|
||||
* Rope 0000 0000
|
||||
* Linear - !0000
|
||||
* HasBase - xxx1
|
||||
* Dependent 0001 0001
|
||||
* Flat - isLinear && !isDependent
|
||||
* Undepended 0011 0011
|
||||
* Extensible 0010 0010
|
||||
* Inline 0100 isFlat && !isExtensible && (u1.chars == inlineStorage)
|
||||
* FatInline 0100 isInline && header in FINALIZE_FAT_INLINE_STRING arena
|
||||
* External 0100 header in FINALIZE_EXTERNAL_STRING arena
|
||||
* Atom - 1xxx
|
||||
* PermanentAtom 1100 1100
|
||||
* InlineAtom - isAtom && isInline
|
||||
* FatInlineAtom - isAtom && isFatInline
|
||||
* String Instance Subtype
|
||||
* type encoding predicate
|
||||
* ------------------------------------
|
||||
* Rope 000000 000000
|
||||
* Linear - !000000
|
||||
* HasBase - xxxx1x
|
||||
* Dependent 000010 000010
|
||||
* Flat - xxxxx1
|
||||
* Undepended 000011 000011
|
||||
* Extensible 010001 010001
|
||||
* Inline 000101 xxx1xx
|
||||
* FatInline 010101 x1x1xx
|
||||
* External 100001 100001
|
||||
* Atom 001001 xx1xxx
|
||||
* PermanentAtom 101001 1x1xxx
|
||||
* InlineAtom - xx11xx
|
||||
* FatInlineAtom - x111xx
|
||||
*
|
||||
* Note that the first 4 flag bits (from right to left in the previous table)
|
||||
* have the following meaning and can be used for some hot queries:
|
||||
*
|
||||
* Bit 0: IsFlat
|
||||
* Bit 1: HasBase (Dependent, Undepended)
|
||||
* Bit 2: IsInline (Inline, FatInline)
|
||||
* Bit 3: IsAtom (Atom, PermanentAtom)
|
||||
*
|
||||
* "HasBase" here refers to the two string types that have a 'base' field:
|
||||
* JSDependentString and JSUndependedString.
|
||||
|
@ -200,31 +210,25 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
*
|
||||
*/
|
||||
|
||||
static const size_t LENGTH_SHIFT = 4;
|
||||
static const size_t FLAGS_MASK = JS_BITMASK(LENGTH_SHIFT);
|
||||
static const uint32_t FLAT_BIT = JS_BIT(0);
|
||||
static const uint32_t HAS_BASE_BIT = JS_BIT(1);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
|
||||
static const uint32_t ATOM_BIT = JS_BIT(3);
|
||||
|
||||
static const size_t ROPE_FLAGS = 0;
|
||||
static const size_t DEPENDENT_FLAGS = JS_BIT(0);
|
||||
static const size_t UNDEPENDED_FLAGS = JS_BIT(0) | JS_BIT(1);
|
||||
static const size_t EXTENSIBLE_FLAGS = JS_BIT(1);
|
||||
static const size_t FIXED_FLAGS = JS_BIT(2);
|
||||
static const uint32_t ROPE_FLAGS = 0;
|
||||
static const uint32_t DEPENDENT_FLAGS = HAS_BASE_BIT;
|
||||
static const uint32_t UNDEPENDED_FLAGS = FLAT_BIT | HAS_BASE_BIT;
|
||||
static const uint32_t EXTENSIBLE_FLAGS = FLAT_BIT | JS_BIT(4);
|
||||
static const uint32_t EXTERNAL_FLAGS = FLAT_BIT | JS_BIT(5);
|
||||
|
||||
static const size_t INT32_MASK = JS_BITMASK(3);
|
||||
static const size_t INT32_FLAGS = JS_BIT(1) | JS_BIT(2);
|
||||
static const uint32_t FAT_INLINE_MASK = INLINE_CHARS_BIT | JS_BIT(4);
|
||||
static const uint32_t PERMANENT_ATOM_MASK = ATOM_BIT | JS_BIT(5);
|
||||
|
||||
static const size_t HAS_BASE_BIT = JS_BIT(0);
|
||||
static const size_t PERMANENT_BIT = JS_BIT(2);
|
||||
static const size_t ATOM_BIT = JS_BIT(3);
|
||||
/* Initial flags for inline and fat inline strings. */
|
||||
static const uint32_t INIT_INLINE_FLAGS = FLAT_BIT | INLINE_CHARS_BIT;
|
||||
static const uint32_t INIT_FAT_INLINE_FLAGS = FLAT_BIT | FAT_INLINE_MASK;
|
||||
|
||||
static const size_t PERMANENT_ATOM_FLAGS = JS_BIT(2) | JS_BIT(3);
|
||||
|
||||
static const size_t MAX_LENGTH = JS_BIT(32 - LENGTH_SHIFT) - 1;
|
||||
|
||||
size_t buildLengthAndFlags(size_t length, size_t flags) {
|
||||
JS_ASSERT(length <= MAX_LENGTH);
|
||||
JS_ASSERT(flags <= FLAGS_MASK);
|
||||
return (length << LENGTH_SHIFT) | flags;
|
||||
}
|
||||
static const uint32_t MAX_LENGTH = JS_BIT(28) - 1;
|
||||
|
||||
/*
|
||||
* Helper function to validate that a string of a given length is
|
||||
|
@ -234,13 +238,23 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
static inline bool validateLength(js::ThreadSafeContext *maybecx, size_t length);
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
|
||||
JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >>
|
||||
JSString::LENGTH_SHIFT) == JSString::MAX_LENGTH);
|
||||
JS_STATIC_ASSERT(sizeof(JSString) ==
|
||||
offsetof(JSString, d.inlineStorage) + NUM_INLINE_CHARS * sizeof(jschar));
|
||||
JS_STATIC_ASSERT(offsetof(JSString, d.u1.chars) ==
|
||||
offsetof(js::shadow::Atom, chars));
|
||||
static_assert(JSString::MAX_LENGTH < UINT32_MAX, "Length must fit in 32 bits");
|
||||
static_assert(sizeof(JSString) ==
|
||||
offsetof(JSString, d.inlineStorage) + NUM_INLINE_CHARS * sizeof(jschar),
|
||||
"NUM_INLINE_CHARS inline chars must fit in a JSString");
|
||||
|
||||
/* Ensure js::shadow::Atom has the same layout. */
|
||||
using js::shadow::Atom;
|
||||
static_assert(offsetof(JSString, d.u1.length) == offsetof(Atom, length),
|
||||
"shadow::Atom length offset must match JSString");
|
||||
static_assert(offsetof(JSString, d.u1.flags) == offsetof(Atom, flags),
|
||||
"shadow::Atom flags offset must match JSString");
|
||||
static_assert(offsetof(JSString, d.s.u2.nonInlineChars) == offsetof(Atom, nonInlineChars),
|
||||
"shadow::Atom nonInlineChars offset must match JSString");
|
||||
static_assert(offsetof(JSString, d.inlineStorage) == offsetof(Atom, inlineStorage),
|
||||
"shadow::Atom inlineStorage offset must match JSString");
|
||||
static_assert(INLINE_CHARS_BIT == Atom::INLINE_CHARS_BIT,
|
||||
"shadow::Atom::INLINE_CHARS_BIT must match JSString::INLINE_CHARS_BIT");
|
||||
}
|
||||
|
||||
/* Avoid lame compile errors in JSRope::flatten */
|
||||
|
@ -251,12 +265,12 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
size_t length() const {
|
||||
return d.u0.lengthAndFlags >> LENGTH_SHIFT;
|
||||
return d.u1.length;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool empty() const {
|
||||
return d.u0.lengthAndFlags <= FLAGS_MASK;
|
||||
return d.u1.length == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -298,7 +312,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isRope() const {
|
||||
return (d.u0.lengthAndFlags & FLAGS_MASK) == ROPE_FLAGS;
|
||||
return d.u1.flags == ROPE_FLAGS;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -320,7 +334,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isDependent() const {
|
||||
return (d.u0.lengthAndFlags & FLAGS_MASK) == DEPENDENT_FLAGS;
|
||||
return d.u1.flags == DEPENDENT_FLAGS;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -331,7 +345,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isFlat() const {
|
||||
return isLinear() && !isDependent();
|
||||
return d.u1.flags & FLAT_BIT;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -342,7 +356,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isExtensible() const {
|
||||
return (d.u0.lengthAndFlags & FLAGS_MASK) == EXTENSIBLE_FLAGS;
|
||||
return d.u1.flags == EXTENSIBLE_FLAGS;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -353,7 +367,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isInline() const {
|
||||
return isFlat() && !isExtensible() && (d.u1.chars == d.inlineStorage);
|
||||
return d.u1.flags & INLINE_CHARS_BIT;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -362,10 +376,15 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
return *(JSInlineString *)this;
|
||||
}
|
||||
|
||||
bool isFatInline() const;
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isFatInline() const {
|
||||
return (d.u1.flags & FAT_INLINE_MASK) == FAT_INLINE_MASK;
|
||||
}
|
||||
|
||||
/* For hot code, prefer other type queries. */
|
||||
bool isExternal() const;
|
||||
bool isExternal() const {
|
||||
return d.u1.flags == EXTERNAL_FLAGS;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
JSExternalString &asExternal() const {
|
||||
|
@ -375,17 +394,17 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isUndepended() const {
|
||||
return (d.u0.lengthAndFlags & FLAGS_MASK) == UNDEPENDED_FLAGS;
|
||||
return d.u1.flags == UNDEPENDED_FLAGS;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isAtom() const {
|
||||
return d.u0.lengthAndFlags & ATOM_BIT;
|
||||
return d.u1.flags & ATOM_BIT;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isPermanentAtom() const {
|
||||
return (d.u0.lengthAndFlags & FLAGS_MASK) == PERMANENT_ATOM_FLAGS;
|
||||
return (d.u1.flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_MASK;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -397,8 +416,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
/* Only called by the GC for dependent or undepended strings. */
|
||||
|
||||
inline bool hasBase() const {
|
||||
JS_STATIC_ASSERT((DEPENDENT_FLAGS | JS_BIT(1)) == UNDEPENDED_FLAGS);
|
||||
return d.u0.lengthAndFlags & HAS_BASE_BIT;
|
||||
return d.u1.flags & HAS_BASE_BIT;
|
||||
}
|
||||
|
||||
inline JSLinearString *base() const;
|
||||
|
@ -415,12 +433,15 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
/* Offsets for direct field from jit code. */
|
||||
|
||||
static size_t offsetOfLengthAndFlags() {
|
||||
return offsetof(JSString, d.u0.lengthAndFlags);
|
||||
static size_t offsetOfLength() {
|
||||
return offsetof(JSString, d.u1.length);
|
||||
}
|
||||
static size_t offsetOfFlags() {
|
||||
return offsetof(JSString, d.u1.flags);
|
||||
}
|
||||
|
||||
static size_t offsetOfChars() {
|
||||
return offsetof(JSString, d.u1.chars);
|
||||
static size_t offsetOfNonInlineChars() {
|
||||
return offsetof(JSString, d.s.u2.nonInlineChars);
|
||||
}
|
||||
|
||||
js::gc::AllocKind getAllocKind() const { return tenuredGetAllocKind(); }
|
||||
|
@ -483,21 +504,21 @@ class JSRope : public JSString
|
|||
|
||||
inline JSString *leftChild() const {
|
||||
JS_ASSERT(isRope());
|
||||
return d.u1.left;
|
||||
return d.s.u2.left;
|
||||
}
|
||||
|
||||
inline JSString *rightChild() const {
|
||||
JS_ASSERT(isRope());
|
||||
return d.s.u2.right;
|
||||
return d.s.u3.right;
|
||||
}
|
||||
|
||||
inline void markChildren(JSTracer *trc);
|
||||
|
||||
inline static size_t offsetOfLeft() {
|
||||
return offsetof(JSRope, d.u1.left);
|
||||
return offsetof(JSRope, d.s.u2.left);
|
||||
}
|
||||
inline static size_t offsetOfRight() {
|
||||
return offsetof(JSRope, d.s.u2.right);
|
||||
return offsetof(JSRope, d.s.u3.right);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -514,14 +535,17 @@ class JSLinearString : public JSString
|
|||
|
||||
public:
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *chars() const {
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
return d.u1.chars;
|
||||
const jschar *nonInlineChars() const {
|
||||
JS_ASSERT(!isInline());
|
||||
return d.s.u2.nonInlineChars;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *chars() const;
|
||||
|
||||
JS::TwoByteChars range() const {
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
return JS::TwoByteChars(d.u1.chars, length());
|
||||
return JS::TwoByteChars(chars(), length());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -541,6 +565,9 @@ class JSDependentString : public JSLinearString
|
|||
bool isDependent() const MOZ_DELETE;
|
||||
JSDependentString &asDependent() const MOZ_DELETE;
|
||||
|
||||
/* Hide chars(), nonInlineChars() is more efficient. */
|
||||
const jschar *chars() const MOZ_DELETE;
|
||||
|
||||
public:
|
||||
static inline JSLinearString *new_(js::ExclusiveContext *cx, JSLinearString *base,
|
||||
const jschar *chars, size_t length);
|
||||
|
@ -593,11 +620,11 @@ class JSFlatString : public JSLinearString
|
|||
* operation changes the string to the JSAtom type, in place.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE JSAtom *morphAtomizedStringIntoAtom() {
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length(), ATOM_BIT);
|
||||
d.u1.flags |= ATOM_BIT;
|
||||
return &asAtom();
|
||||
}
|
||||
MOZ_ALWAYS_INLINE JSAtom *morphAtomizedStringIntoPermanentAtom() {
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length(), PERMANENT_ATOM_FLAGS);
|
||||
d.u1.flags |= PERMANENT_ATOM_MASK;
|
||||
return &asAtom();
|
||||
}
|
||||
|
||||
|
@ -612,11 +639,14 @@ class JSExtensibleString : public JSFlatString
|
|||
bool isExtensible() const MOZ_DELETE;
|
||||
JSExtensibleString &asExtensible() const MOZ_DELETE;
|
||||
|
||||
/* Hide chars(), nonInlineChars() is more efficient. */
|
||||
const jschar *chars() const MOZ_DELETE;
|
||||
|
||||
public:
|
||||
MOZ_ALWAYS_INLINE
|
||||
size_t capacity() const {
|
||||
JS_ASSERT(JSString::isExtensible());
|
||||
return d.s.u2.capacity;
|
||||
return d.s.u3.capacity;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -627,6 +657,9 @@ class JSInlineString : public JSFlatString
|
|||
{
|
||||
static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
|
||||
|
||||
/* Hide chars(), inlineChars() is more efficient. */
|
||||
const jschar *chars() const MOZ_DELETE;
|
||||
|
||||
public:
|
||||
template <js::AllowGC allowGC>
|
||||
static inline JSInlineString *new_(js::ThreadSafeContext *cx);
|
||||
|
@ -635,6 +668,12 @@ class JSInlineString : public JSFlatString
|
|||
|
||||
inline void resetLength(size_t length);
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *inlineChars() const {
|
||||
const char *p = reinterpret_cast<const char *>(this);
|
||||
return reinterpret_cast<const jschar *>(p + offsetOfInlineStorage());
|
||||
}
|
||||
|
||||
static bool lengthFits(size_t length) {
|
||||
return length <= MAX_INLINE_LENGTH;
|
||||
}
|
||||
|
@ -668,6 +707,9 @@ class JSFatInlineString : public JSInlineString
|
|||
offsetof(JSFatInlineString, d.inlineStorage)) / sizeof(jschar));
|
||||
}
|
||||
|
||||
/* Hide chars(), inlineChars() is more efficient. */
|
||||
const jschar *chars() const MOZ_DELETE;
|
||||
|
||||
protected: /* to fool clang into not warning this is unused */
|
||||
jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
|
||||
|
||||
|
@ -679,6 +721,8 @@ class JSFatInlineString : public JSInlineString
|
|||
INLINE_EXTENSION_CHARS
|
||||
-1 /* null terminator */;
|
||||
|
||||
inline jschar *init(size_t length);
|
||||
|
||||
static bool lengthFits(size_t length) {
|
||||
return length <= MAX_FAT_INLINE_LENGTH;
|
||||
}
|
||||
|
@ -698,13 +742,16 @@ class JSExternalString : public JSFlatString
|
|||
bool isExternal() const MOZ_DELETE;
|
||||
JSExternalString &asExternal() const MOZ_DELETE;
|
||||
|
||||
/* Hide chars(), nonInlineChars() is more efficient. */
|
||||
const jschar *chars() const MOZ_DELETE;
|
||||
|
||||
public:
|
||||
static inline JSExternalString *new_(JSContext *cx, const jschar *chars, size_t length,
|
||||
const JSStringFinalizer *fin);
|
||||
|
||||
const JSStringFinalizer *externalFinalizer() const {
|
||||
JS_ASSERT(JSString::isExternal());
|
||||
return d.s.u2.externalFinalizer;
|
||||
return d.s.u3.externalFinalizer;
|
||||
}
|
||||
|
||||
/* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
|
||||
|
@ -739,13 +786,13 @@ class JSAtom : public JSFlatString
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isPermanent() const {
|
||||
return d.u0.lengthAndFlags & PERMANENT_BIT;
|
||||
return JSString::isPermanentAtom();
|
||||
}
|
||||
|
||||
// Transform this atom into a permanent atom. This is only done during
|
||||
// initialization of the runtime.
|
||||
MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
|
||||
d.u0.lengthAndFlags = buildLengthAndFlags(length(), PERMANENT_ATOM_FLAGS);
|
||||
d.u1.flags |= PERMANENT_ATOM_MASK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -1048,8 +1095,15 @@ inline JSLinearString *
|
|||
JSString::base() const
|
||||
{
|
||||
JS_ASSERT(hasBase());
|
||||
JS_ASSERT(!d.s.u2.base->isInline());
|
||||
return d.s.u2.base;
|
||||
JS_ASSERT(!d.s.u3.base->isInline());
|
||||
return d.s.u3.base;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE const jschar *
|
||||
JSLinearString::chars() const
|
||||
{
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
return isInline() ? asInline().inlineChars() : nonInlineChars();
|
||||
}
|
||||
|
||||
inline js::PropertyName *
|
||||
|
|
|
@ -137,6 +137,7 @@ namespace jit {
|
|||
_(RenumberBlocks) \
|
||||
_(DominatorTree) \
|
||||
_(PhiAnalysis) \
|
||||
_(MakeLoopsContiguous) \
|
||||
_(ApplyTypes) \
|
||||
_(ParallelSafetyAnalysis) \
|
||||
_(AliasAnalysis) \
|
||||
|
|
|
@ -12,7 +12,14 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
typedef WeakMap<PreBarrieredObject, RelocatableValue> ObjectValueMap;
|
||||
class ObjectValueMap : public WeakMap<PreBarrieredObject, RelocatableValue>
|
||||
{
|
||||
public:
|
||||
ObjectValueMap(JSContext *cx, JSObject *obj)
|
||||
: WeakMap<PreBarrieredObject, RelocatableValue>(cx, obj) {}
|
||||
|
||||
virtual bool findZoneEdges();
|
||||
};
|
||||
|
||||
class WeakMapObject : public JSObject
|
||||
{
|
||||
|
|
|
@ -58,7 +58,6 @@ JS::WeakMapPtr<K, V>::destroy()
|
|||
// of known live weakmaps. If we are, remove ourselves before deleting.
|
||||
if (map->isInList())
|
||||
WeakMapBase::removeWeakMapFromList(map);
|
||||
map->check();
|
||||
js_delete(map);
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
|
|
@ -2131,12 +2131,11 @@ nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent)
|
|||
break;
|
||||
case NS_VK_RETURN:
|
||||
if (IsInDropDownMode()) {
|
||||
if (mComboboxFrame->IsDroppedDown()) {
|
||||
// If the select element is a dropdown style, Enter key should be
|
||||
// consumed everytime since Enter key may be pressed accidentally after
|
||||
// the dropdown is closed by Enter key press.
|
||||
// consumed while the dropdown is open for security.
|
||||
aKeyEvent->PreventDefault();
|
||||
|
||||
if (mComboboxFrame->IsDroppedDown()) {
|
||||
nsWeakFrame weakFrame(this);
|
||||
ComboboxFinish(mEndSelectionIndex);
|
||||
if (!weakFrame.IsAlive()) {
|
||||
|
|
|
@ -321,7 +321,7 @@ function runTests()
|
|||
|
||||
reset()
|
||||
synthesizeKey("VK_RETURN", {});
|
||||
check(true, "Enter key on combobox");
|
||||
check(false, "Enter key on combobox");
|
||||
|
||||
reset()
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
|
|
|
@ -133,6 +133,8 @@ public:
|
|||
lock.NotifyAll(); // In case Run() is waiting.
|
||||
|
||||
if (mThread != nullptr) {
|
||||
MonitorAutoUnlock unlock(mMonitor);
|
||||
CODEC_LOGD("OMXOutputDrain thread shutdown");
|
||||
mThread->Shutdown();
|
||||
mThread = nullptr;
|
||||
}
|
||||
|
@ -691,6 +693,11 @@ WebrtcOMXH264VideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
|
|||
this, mWidth, mHeight);
|
||||
}
|
||||
|
||||
if (aFrameTypes && aFrameTypes->size() &&
|
||||
((*aFrameTypes)[0] == webrtc::kKeyFrame)) {
|
||||
mOMX->RequestIDRFrame();
|
||||
}
|
||||
|
||||
// Wrap I420VideoFrame input with PlanarYCbCrImage for OMXVideoEncoder.
|
||||
layers::PlanarYCbCrData yuvData;
|
||||
yuvData.mYChannel = const_cast<uint8_t*>(aInputImage.buffer(webrtc::kYPlane));
|
||||
|
|
|
@ -52,7 +52,7 @@ static int64_t kAccessUnitTimeoutUs = 10000000ll;
|
|||
|
||||
// If no access units arrive for the first 10 secs after starting the
|
||||
// stream, assume none ever will and signal EOS or switch transports.
|
||||
static int64_t kStartupTimeoutUs = 10000000ll;
|
||||
static int64_t kPlayTimeoutUs = 10000000ll;
|
||||
|
||||
static int64_t kDefaultKeepAliveTimeoutUs = 60000000ll;
|
||||
|
||||
|
@ -109,6 +109,7 @@ struct RtspConnectionHandler : public AHandler {
|
|||
kWhatEOS = 'eos!',
|
||||
kWhatSeekDiscontinuity = 'seeD',
|
||||
kWhatNormalPlayTimeMapping = 'nptM',
|
||||
kWhatTryTCPInterleaving = 'ttiL',
|
||||
};
|
||||
|
||||
RtspConnectionHandler(
|
||||
|
@ -140,7 +141,8 @@ struct RtspConnectionHandler : public AHandler {
|
|||
mReceivedFirstRTPPacket(false),
|
||||
mSeekable(false),
|
||||
mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs),
|
||||
mKeepAliveGeneration(0) {
|
||||
mKeepAliveGeneration(0),
|
||||
mNumPlayTimeoutsPending(0) {
|
||||
mNetLooper->setName("rtsp net");
|
||||
mNetLooper->start(false /* runOnCallingThread */,
|
||||
false /* canCallJava */,
|
||||
|
@ -703,9 +705,17 @@ struct RtspConnectionHandler : public AHandler {
|
|||
} else {
|
||||
msg->setInt32("isSeekable", 0);
|
||||
}
|
||||
// Notify the Rtsp Controller that we are ready to play.
|
||||
// Notify RTSPSource that we are ready to play.
|
||||
msg->setInt32("what", kWhatConnected);
|
||||
msg->post();
|
||||
|
||||
// Notify RTSPSource that we are trying TCP interleaving and
|
||||
// ready to play again.
|
||||
if (mTryTCPInterleaving) {
|
||||
sp<AMessage> msgTryTcp = mNotify->dup();
|
||||
msgTryTcp->setInt32("what", kWhatTryTCPInterleaving);
|
||||
msgTryTcp->post();
|
||||
}
|
||||
} else {
|
||||
sp<AMessage> reply = new AMessage('disc', id());
|
||||
reply->setInt32("result", result);
|
||||
|
@ -748,8 +758,9 @@ struct RtspConnectionHandler : public AHandler {
|
|||
parsePlayResponse(response);
|
||||
|
||||
sp<AMessage> timeout = new AMessage('tiou', id());
|
||||
timeout->post(kStartupTimeoutUs);
|
||||
timeout->post(kPlayTimeoutUs);
|
||||
mPausePending = false;
|
||||
mNumPlayTimeoutsPending++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1133,6 +1144,20 @@ struct RtspConnectionHandler : public AHandler {
|
|||
|
||||
case 'tiou':
|
||||
{
|
||||
CHECK(mNumPlayTimeoutsPending >= 1);
|
||||
mNumPlayTimeoutsPending--;
|
||||
// If there are more than one pending 'tiou' messages in the
|
||||
// queue, we ignore the preceding ones and only handle the last
|
||||
// one.
|
||||
// This check is necessary when we fail back to using RTP
|
||||
// interleaved in the existing RTSP connection. It prevents from
|
||||
// aboring a connection that is trying to transport RTP over
|
||||
// TCP.
|
||||
if (mNumPlayTimeoutsPending > 0) {
|
||||
// Do nothing. We only handle the last 'tiou' message.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mReceivedFirstRTCPPacket) {
|
||||
if (mReceivedFirstRTPPacket && !mTryFakeRTCP) {
|
||||
LOGW("We received RTP packets but no RTCP packets, "
|
||||
|
@ -1353,6 +1378,7 @@ private:
|
|||
bool mSeekable;
|
||||
int64_t mKeepAliveTimeoutUs;
|
||||
int32_t mKeepAliveGeneration;
|
||||
int32_t mNumPlayTimeoutsPending;
|
||||
|
||||
Vector<TrackInfo> mTracks;
|
||||
|
||||
|
|
|
@ -471,6 +471,22 @@ void RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
|
|||
break;
|
||||
}
|
||||
|
||||
case RtspConnectionHandler::kWhatTryTCPInterleaving:
|
||||
{
|
||||
// By default, we will request to deliver RTP over UDP. If the play
|
||||
// request timed out and we didn't receive any RTP packet, we will
|
||||
// fail back to use RTP interleaved in the existing RTSP/TCP
|
||||
// connection. And in this case, we have to explicitly perform
|
||||
// another play event to request the server to start streaming
|
||||
// again.
|
||||
int64_t playTimeUs;
|
||||
if (!msg->findInt64("timeUs", &playTimeUs)) {
|
||||
playTimeUs = 0;
|
||||
}
|
||||
performPlay(playTimeUs);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
TRESPASS();
|
||||
}
|
||||
|
|
|
@ -605,8 +605,9 @@ VerifySignature(AppTrustedRoot trustedRoot,
|
|||
}
|
||||
if (BuildCertChain(trustDomain, signerCert, PR_Now(),
|
||||
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
|
||||
SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
|
||||
SEC_OID_X509_ANY_POLICY, nullptr, builtChain)
|
||||
KeyPurposeId::id_kp_codeSigning,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr, builtChain)
|
||||
!= SECSuccess) {
|
||||
return MapSECStatus(SECFailure);
|
||||
}
|
||||
|
|
|
@ -103,15 +103,15 @@ AppTrustDomain::FindPotentialIssuers(const SECItem* encodedIssuerName,
|
|||
|
||||
SECStatus
|
||||
AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
SECOidTag policy,
|
||||
const CertPolicyId& policy,
|
||||
const CERTCertificate* candidateCert,
|
||||
/*out*/ TrustLevel* trustLevel)
|
||||
{
|
||||
MOZ_ASSERT(policy == SEC_OID_X509_ANY_POLICY);
|
||||
MOZ_ASSERT(policy.IsAnyPolicy());
|
||||
MOZ_ASSERT(candidateCert);
|
||||
MOZ_ASSERT(trustLevel);
|
||||
MOZ_ASSERT(mTrustedRoot);
|
||||
if (!candidateCert || !trustLevel || policy != SEC_OID_X509_ANY_POLICY) {
|
||||
if (!candidateCert || !trustLevel || !policy.IsAnyPolicy()) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot);
|
||||
|
||||
SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
SECOidTag policy,
|
||||
const mozilla::pkix::CertPolicyId& policy,
|
||||
const CERTCertificate* candidateCert,
|
||||
/*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE;
|
||||
SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
|
||||
|
|
|
@ -300,8 +300,8 @@ destroyCertListThatShouldNotExist(CERTCertList** certChain)
|
|||
static SECStatus
|
||||
BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
|
||||
PRTime time, KeyUsages ku1, KeyUsages ku2,
|
||||
KeyUsages ku3, SECOidTag eku,
|
||||
SECOidTag requiredPolicy,
|
||||
KeyUsages ku3, KeyPurposeId eku,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
const SECItem* stapledOCSPResponse,
|
||||
ScopedCERTCertList& builtChain)
|
||||
{
|
||||
|
@ -391,9 +391,9 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
pinArg);
|
||||
rv = BuildCertChain(trustDomain, cert, time,
|
||||
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
|
||||
SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
|
||||
SEC_OID_X509_ANY_POLICY,
|
||||
stapledOCSPResponse, builtChain);
|
||||
KeyPurposeId::id_kp_clientAuth,
|
||||
CertPolicyId::anyPolicy, stapledOCSPResponse,
|
||||
builtChain);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -404,9 +404,10 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
// Try to validate for EV first.
|
||||
SECOidTag evPolicy = SEC_OID_UNKNOWN;
|
||||
rv = GetFirstEVPolicy(cert, evPolicy);
|
||||
if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) {
|
||||
CertPolicyId evPolicy;
|
||||
SECOidTag evPolicyOidTag;
|
||||
rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
|
||||
if (rv == SECSuccess) {
|
||||
NSSCertDBTrustDomain
|
||||
trustDomain(trustSSL,
|
||||
ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
|
||||
|
@ -417,12 +418,12 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
KU_DIGITAL_SIGNATURE, // ECDHE/DHE
|
||||
KU_KEY_ENCIPHERMENT, // RSA
|
||||
KU_KEY_AGREEMENT, // ECDH/DH
|
||||
SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
|
||||
KeyPurposeId::id_kp_serverAuth,
|
||||
evPolicy, stapledOCSPResponse,
|
||||
builtChain);
|
||||
if (rv == SECSuccess) {
|
||||
if (evOidPolicy) {
|
||||
*evOidPolicy = evPolicy;
|
||||
*evOidPolicy = evPolicyOidTag;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -443,8 +444,8 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
KU_DIGITAL_SIGNATURE, // ECDHE/DHE
|
||||
KU_KEY_ENCIPHERMENT, // RSA
|
||||
KU_KEY_AGREEMENT, // ECDH/DH
|
||||
SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
|
||||
SEC_OID_X509_ANY_POLICY,
|
||||
KeyPurposeId::id_kp_serverAuth,
|
||||
CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
break;
|
||||
}
|
||||
|
@ -453,9 +454,8 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
|
||||
pinArg);
|
||||
rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
|
||||
KU_KEY_CERT_SIGN,
|
||||
SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
|
||||
SEC_OID_X509_ANY_POLICY,
|
||||
KU_KEY_CERT_SIGN, KeyPurposeId::id_kp_serverAuth,
|
||||
CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
break;
|
||||
}
|
||||
|
@ -465,8 +465,8 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
pinArg);
|
||||
rv = BuildCertChain(trustDomain, cert, time,
|
||||
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
|
||||
SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
|
||||
SEC_OID_X509_ANY_POLICY,
|
||||
KeyPurposeId::id_kp_emailProtection,
|
||||
CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
break;
|
||||
}
|
||||
|
@ -481,8 +481,8 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
KU_KEY_ENCIPHERMENT, // RSA
|
||||
KU_KEY_AGREEMENT, // ECDH/DH
|
||||
0,
|
||||
SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
|
||||
SEC_OID_X509_ANY_POLICY,
|
||||
KeyPurposeId::id_kp_emailProtection,
|
||||
CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
break;
|
||||
}
|
||||
|
@ -492,8 +492,8 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
mOCSPCache, pinArg);
|
||||
rv = BuildCertChain(trustDomain, cert, time,
|
||||
EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
|
||||
SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
|
||||
SEC_OID_X509_ANY_POLICY,
|
||||
KeyPurposeId::id_kp_codeSigning,
|
||||
CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
break;
|
||||
}
|
||||
|
@ -506,34 +506,34 @@ CertVerifier::MozillaPKIXVerifyCert(
|
|||
// interesting, we just try them all.
|
||||
mozilla::pkix::EndEntityOrCA endEntityOrCA;
|
||||
mozilla::pkix::KeyUsages keyUsage;
|
||||
SECOidTag eku;
|
||||
KeyPurposeId eku;
|
||||
if (usage == certificateUsageVerifyCA) {
|
||||
endEntityOrCA = EndEntityOrCA::MustBeCA;
|
||||
keyUsage = KU_KEY_CERT_SIGN;
|
||||
eku = SEC_OID_UNKNOWN;
|
||||
eku = KeyPurposeId::anyExtendedKeyUsage;
|
||||
} else {
|
||||
endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
|
||||
keyUsage = KU_DIGITAL_SIGNATURE;
|
||||
eku = SEC_OID_OCSP_RESPONDER;
|
||||
eku = KeyPurposeId::id_kp_OCSPSigning;
|
||||
}
|
||||
|
||||
NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
|
||||
pinArg);
|
||||
rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
|
||||
keyUsage, eku, SEC_OID_X509_ANY_POLICY,
|
||||
keyUsage, eku, CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
|
||||
NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
|
||||
pinArg);
|
||||
rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
|
||||
eku, SEC_OID_X509_ANY_POLICY,
|
||||
eku, CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
|
||||
NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
|
||||
ocspFetching, mOCSPCache,
|
||||
pinArg);
|
||||
rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
|
||||
keyUsage, eku, SEC_OID_X509_ANY_POLICY,
|
||||
keyUsage, eku, CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse, builtChain);
|
||||
}
|
||||
}
|
||||
|
@ -616,7 +616,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
|
|||
|
||||
// Do EV checking only for sslserver usage
|
||||
if (usage == certificateUsageSSLServer) {
|
||||
SECStatus srv = GetFirstEVPolicy(cert, evPolicy);
|
||||
CertPolicyId unusedPolicyId;
|
||||
SECStatus srv = GetFirstEVPolicy(cert, unusedPolicyId, evPolicy);
|
||||
if (srv == SECSuccess) {
|
||||
if (evPolicy != SEC_OID_UNKNOWN) {
|
||||
trustAnchors = GetRootsForOid(evPolicy);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "certdb.h"
|
||||
#include "base64.h"
|
||||
#include "pkix/nullptr.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "pk11pub.h"
|
||||
#include "secerr.h"
|
||||
#include "prerror.h"
|
||||
|
@ -880,21 +881,23 @@ GetRootsForOid(SECOidTag oid_tag)
|
|||
|
||||
bool
|
||||
CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert,
|
||||
SECOidTag policyOidTag)
|
||||
const mozilla::pkix::CertPolicyId& policy)
|
||||
{
|
||||
PR_ASSERT(cert);
|
||||
PR_ASSERT(policyOidTag != SEC_OID_UNKNOWN);
|
||||
if (!cert || !policyOidTag) {
|
||||
if (!cert) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) {
|
||||
nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
|
||||
if (entry.oid_tag == policyOidTag && entry.cert &&
|
||||
CERT_CompareCerts(cert, entry.cert)) {
|
||||
if (entry.cert && CERT_CompareCerts(cert, entry.cert)) {
|
||||
const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag);
|
||||
if (oidData && oidData->oid.len == policy.numBytes &&
|
||||
!memcmp(oidData->oid.data, policy.bytes, policy.numBytes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -945,7 +948,8 @@ IdentityInfoInit()
|
|||
|
||||
unsigned char certFingerprint[20];
|
||||
rv = PK11_HashBuf(SEC_OID_SHA1, certFingerprint,
|
||||
entry.cert->derCert.data, entry.cert->derCert.len);
|
||||
entry.cert->derCert.data,
|
||||
static_cast<int32_t>(entry.cert->derCert.len));
|
||||
PR_ASSERT(rv == SECSuccess);
|
||||
if (rv == SECSuccess) {
|
||||
bool same = !memcmp(certFingerprint, entry.ev_root_sha1_fingerprint, 20);
|
||||
|
@ -1005,10 +1009,14 @@ CleanupIdentityInfo()
|
|||
|
||||
// Find the first policy OID that is known to be an EV policy OID.
|
||||
SECStatus
|
||||
GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag)
|
||||
GetFirstEVPolicy(CERTCertificate* cert,
|
||||
/*out*/ mozilla::pkix::CertPolicyId& policy,
|
||||
/*out*/ SECOidTag& policyOidTag)
|
||||
{
|
||||
if (!cert)
|
||||
if (!cert) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (cert->extensions) {
|
||||
for (int i=0; cert->extensions[i]; i++) {
|
||||
|
@ -1035,18 +1043,29 @@ GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag)
|
|||
|
||||
SECOidTag oid_tag = policyInfo->oid;
|
||||
if (oid_tag != SEC_OID_UNKNOWN && isEVPolicy(oid_tag)) {
|
||||
// in our list of OIDs accepted for EV
|
||||
outOidTag = oid_tag;
|
||||
const SECOidData* oidData = SECOID_FindOIDByTag(oid_tag);
|
||||
PR_ASSERT(oidData);
|
||||
PR_ASSERT(oidData->oid.data);
|
||||
PR_ASSERT(oidData->oid.len > 0);
|
||||
PR_ASSERT(oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES);
|
||||
if (oidData && oidData->oid.data && oidData->oid.len > 0 &&
|
||||
oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES) {
|
||||
policy.numBytes = static_cast<uint16_t>(oidData->oid.len);
|
||||
memcpy(policy.bytes, oidData->oid.data, policy.numBytes);
|
||||
policyOidTag = oid_tag;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
CERT_DestroyCertificatePoliciesExtension(policies);
|
||||
if (found)
|
||||
if (found) {
|
||||
return SECSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,17 +9,21 @@
|
|||
#include "certt.h"
|
||||
#include "prtypes.h"
|
||||
|
||||
namespace mozilla { namespace pkix { struct CertPolicyId; } }
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
void EnsureIdentityInfoLoaded();
|
||||
void CleanupIdentityInfo();
|
||||
SECStatus GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag);
|
||||
SECStatus GetFirstEVPolicy(CERTCertificate* cert,
|
||||
/*out*/ mozilla::pkix::CertPolicyId& policy,
|
||||
/*out*/ SECOidTag& policyOidTag);
|
||||
|
||||
// CertIsAuthoritativeForEVPolicy does NOT evaluate whether the cert is trusted
|
||||
// or distrusted.
|
||||
bool CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert,
|
||||
SECOidTag policyOidTag);
|
||||
const mozilla::pkix::CertPolicyId& policy);
|
||||
#endif
|
||||
|
||||
#ifndef NSS_NO_LIBPKIX
|
||||
|
|
|
@ -68,7 +68,7 @@ NSSCertDBTrustDomain::FindPotentialIssuers(
|
|||
|
||||
SECStatus
|
||||
NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
SECOidTag policy,
|
||||
const CertPolicyId& policy,
|
||||
const CERTCertificate* candidateCert,
|
||||
/*out*/ TrustLevel* trustLevel)
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
}
|
||||
|
||||
#ifdef MOZ_NO_EV_CERTS
|
||||
if (policy != SEC_OID_X509_ANY_POLICY) {
|
||||
if (!policy.IsAnyPolicy()) {
|
||||
PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
// needed to consider end-entity certs to be their own trust anchors since
|
||||
// Gecko implemented nsICertOverrideService.
|
||||
if (flags & CERTDB_TRUSTED_CA) {
|
||||
if (policy == SEC_OID_X509_ANY_POLICY) {
|
||||
if (policy.IsAnyPolicy()) {
|
||||
*trustLevel = TrustLevel::TrustAnchor;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
@ -468,7 +468,7 @@ static char*
|
|||
nss_addEscape(const char* string, char quote)
|
||||
{
|
||||
char* newString = 0;
|
||||
int escapes = 0, size = 0;
|
||||
size_t escapes = 0, size = 0;
|
||||
const char* src;
|
||||
char* dest;
|
||||
|
||||
|
@ -479,7 +479,7 @@ nss_addEscape(const char* string, char quote)
|
|||
size++;
|
||||
}
|
||||
|
||||
newString = (char*) PORT_ZAlloc(escapes + size + 1);
|
||||
newString = (char*) PORT_ZAlloc(escapes + size + 1u);
|
||||
if (!newString) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -599,9 +599,9 @@ SetClassicOCSPBehavior(CertVerifier::ocsp_download_config enabled,
|
|||
|
||||
CERT_ForcePostMethodForOCSP(get != CertVerifier::ocsp_get_enabled);
|
||||
|
||||
int OCSPTimeoutSeconds = 3;
|
||||
uint32_t OCSPTimeoutSeconds = 3u;
|
||||
if (strict == CertVerifier::ocsp_strict) {
|
||||
OCSPTimeoutSeconds = 10;
|
||||
OCSPTimeoutSeconds = 10u;
|
||||
}
|
||||
CERT_SetOCSPTimeout(OCSPTimeoutSeconds);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
/*out*/ mozilla::pkix::ScopedCERTCertList& results);
|
||||
|
||||
virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
|
||||
SECOidTag policy,
|
||||
const mozilla::pkix::CertPolicyId& policy,
|
||||
const CERTCertificate* candidateCert,
|
||||
/*out*/ mozilla::pkix::TrustLevel* trustLevel);
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include "OCSPCache.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "pk11pub.h"
|
||||
#include "secerr.h"
|
||||
|
@ -113,30 +115,34 @@ OCSPCache::~OCSPCache()
|
|||
Clear();
|
||||
}
|
||||
|
||||
// Returns -1 if no entry is found for the given (cert, issuer) pair.
|
||||
int32_t
|
||||
// Returns false with index in an undefined state if no matching entry was
|
||||
// found.
|
||||
bool
|
||||
OCSPCache::FindInternal(const CERTCertificate* aCert,
|
||||
const CERTCertificate* aIssuerCert,
|
||||
/*out*/ size_t& index,
|
||||
const MutexAutoLock& /* aProofOfLock */)
|
||||
{
|
||||
if (mEntries.length() == 0) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
SHA384Buffer idHash;
|
||||
SECStatus rv = CertIDHash(idHash, aCert, aIssuerCert);
|
||||
if (rv != SECSuccess) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// mEntries is sorted with the most-recently-used entry at the end.
|
||||
// Thus, searching from the end will often be fastest.
|
||||
for (int32_t i = mEntries.length() - 1; i >= 0; i--) {
|
||||
if (memcmp(mEntries[i]->mIDHash, idHash, SHA384_LENGTH) == 0) {
|
||||
return i;
|
||||
index = mEntries.length();
|
||||
while (index > 0) {
|
||||
--index;
|
||||
if (memcmp(mEntries[index]->mIDHash, idHash, SHA384_LENGTH) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -176,8 +182,8 @@ OCSPCache::Get(const CERTCertificate* aCert,
|
|||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
int32_t index = FindInternal(aCert, aIssuerCert, lock);
|
||||
if (index < 0) {
|
||||
size_t index;
|
||||
if (!FindInternal(aCert, aIssuerCert, index, lock)) {
|
||||
LogWithCerts("OCSPCache::Get(%s, %s) not in cache", aCert, aIssuerCert);
|
||||
return false;
|
||||
}
|
||||
|
@ -200,9 +206,8 @@ OCSPCache::Put(const CERTCertificate* aCert,
|
|||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
int32_t index = FindInternal(aCert, aIssuerCert, lock);
|
||||
|
||||
if (index >= 0) {
|
||||
size_t index;
|
||||
if (FindInternal(aCert, aIssuerCert, index, lock)) {
|
||||
// Never replace an entry indicating a revoked certificate.
|
||||
if (mEntries[index]->mErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) {
|
||||
LogWithCerts("OCSPCache::Put(%s, %s) already in cache as revoked - "
|
||||
|
|
|
@ -92,8 +92,9 @@ private:
|
|||
SHA384Buffer mIDHash;
|
||||
};
|
||||
|
||||
int32_t FindInternal(const CERTCertificate* aCert,
|
||||
bool FindInternal(const CERTCertificate* aCert,
|
||||
const CERTCertificate* aIssuerCert,
|
||||
/*out*/ size_t& index,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
void MakeMostRecentlyUsed(size_t aIndex, const MutexAutoLock& aProofOfLock);
|
||||
void LogWithCerts(const char* aMessage, const CERTCertificate* aCert,
|
||||
|
|
|
@ -24,26 +24,35 @@
|
|||
|
||||
// Work around missing std::bind, std::ref, std::cref in older compilers. This
|
||||
// implementation isn't intended to be complete; rather, it is the minimal
|
||||
// implementation needed to make our use of std::bind work.
|
||||
// implementation needed to make our use of std::bind work for compilers that
|
||||
// lack both C++11 and TR1 support for these features. We cannot even assume
|
||||
// that rvalue references work, which means we don't get perfect forwarding
|
||||
// and thus we basically have to define a new overload for every distinct call
|
||||
// signature.
|
||||
//
|
||||
// A positive side-effect of this code is improved debugging usability; it is
|
||||
// much more convenient to step through code that uses this polyfill than it is
|
||||
// to step through the many nested layers of a real std::bind implementation.
|
||||
//
|
||||
// Build with MOZILLA_PKIX_USE_REAL_FUNCTIONAL defined in order to use the
|
||||
// compiler's definitions of these functions. This is helpful in order to
|
||||
// ensure that the calling code is actually compatible with the real std::bind
|
||||
// and friends.
|
||||
|
||||
#ifndef mozilla_pkix__bind_h
|
||||
#define mozilla_pkix__bind_h
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4275) //Suppress spurious MSVC warning
|
||||
#endif
|
||||
#ifdef MOZILLA_PKIX_USE_REAL_FUNCTIONAL
|
||||
#include <functional>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default:4275)
|
||||
#endif
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef MOZILLA_PKIX_USE_REAL_FUNCTIONAL
|
||||
|
||||
using std::bind;
|
||||
using std::ref;
|
||||
using std::cref;
|
||||
using std::ref;
|
||||
using std::placeholders::_1;
|
||||
|
||||
#else
|
||||
|
@ -66,6 +75,7 @@ public:
|
|||
private:
|
||||
const F f;
|
||||
B1& b1;
|
||||
void operator=(const Bind1&) /*= delete*/;
|
||||
};
|
||||
|
||||
template <typename R, typename P1, typename B1, typename B2>
|
||||
|
@ -79,6 +89,40 @@ private:
|
|||
const F f;
|
||||
B1& b1;
|
||||
B2& b2;
|
||||
void operator=(const Bind2&) /*= delete*/;
|
||||
};
|
||||
|
||||
template <typename R, typename P1, typename B1, typename B2, typename B3>
|
||||
class Bind3
|
||||
{
|
||||
public:
|
||||
typedef R (*F)(P1&, B1, B2&, B3&);
|
||||
Bind3(F f, B1& b1, B2& b2, B3& b3) : f(f), b1(b1), b2(b2), b3(b3) { }
|
||||
R operator()(P1& p1) const { return f(p1, b1, b2, b3); }
|
||||
private:
|
||||
const F f;
|
||||
B1& b1;
|
||||
B2& b2;
|
||||
B3& b3;
|
||||
void operator=(const Bind3&) /*= delete*/;
|
||||
};
|
||||
|
||||
template <typename R, typename P1, typename B1, typename B2, typename B3,
|
||||
typename B4>
|
||||
class Bind4
|
||||
{
|
||||
public:
|
||||
typedef R (*F)(P1&, B1, B2, B3&, B4&);
|
||||
Bind4(F f, B1& b1, B2& b2, B3& b3, B4& b4)
|
||||
: f(f), b1(b1), b2(b2), b3(b3), b4(b4) { }
|
||||
R operator()(P1& p1) const { return f(p1, b1, b2, b3, b4); }
|
||||
private:
|
||||
const F f;
|
||||
B1& b1;
|
||||
B2& b2;
|
||||
B3& b3;
|
||||
B4& b4;
|
||||
void operator=(const Bind4&) /*= delete*/;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -92,12 +136,28 @@ bind(R (*f)(P1&, B1&), Placeholder1&, B1& b1)
|
|||
|
||||
template <typename R, typename P1, typename B1, typename B2>
|
||||
inline internal::Bind2<R, P1, B1, B2>
|
||||
bind(R (*f)(P1&, B1&, B2&), Placeholder1 &, B1 & b1, B2 & b2)
|
||||
bind(R (*f)(P1&, B1&, B2&), Placeholder1&, B1& b1, B2& b2)
|
||||
{
|
||||
return internal::Bind2<R, P1, B1, B2>(f, b1, b2);
|
||||
}
|
||||
|
||||
#endif // _MSC_VER
|
||||
template <typename R, typename P1, typename B1, typename B2, typename B3>
|
||||
inline internal::Bind3<R, P1, const B1, const B2, B3>
|
||||
bind(R (*f)(P1&, B1, const B2&, B3&), Placeholder1&, B1& b1, const B2& b2, B3& b3)
|
||||
{
|
||||
return internal::Bind3<R, P1, const B1, const B2, B3>(f, b1, b2, b3);
|
||||
}
|
||||
|
||||
template <typename R, typename P1, typename B1, typename B2, typename B3,
|
||||
typename B4>
|
||||
inline internal::Bind4<R, P1, const B1, const B2, B3, B4>
|
||||
bind(R (*f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2,
|
||||
B3& b3, B4& b4)
|
||||
{
|
||||
return internal::Bind4<R, P1, const B1, const B2, B3, B4>(f, b1, b2, b3, b4);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} } // namespace mozilla::pkix
|
||||
|
||||
|
|
|
@ -94,8 +94,8 @@ SECStatus BuildCertChain(TrustDomain& trustDomain,
|
|||
PRTime time,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
/*optional*/ KeyUsages requiredKeyUsagesIfPresent,
|
||||
/*optional*/ SECOidTag requiredEKUIfPresent,
|
||||
/*optional*/ SECOidTag requiredPolicy,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const SECItem* stapledOCSPResponse,
|
||||
/*out*/ ScopedCERTCertList& results);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "plarena.h"
|
||||
#include "cert.h"
|
||||
#include "keyhi.h"
|
||||
#include "stdint.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
|
@ -41,9 +42,28 @@ typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
|
|||
typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
|
||||
ScopedSECKEYPublicKey;
|
||||
|
||||
MOZILLA_PKIX_ENUM_CLASS EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
|
||||
|
||||
typedef unsigned int KeyUsages;
|
||||
|
||||
MOZILLA_PKIX_ENUM_CLASS EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
|
||||
MOZILLA_PKIX_ENUM_CLASS KeyPurposeId {
|
||||
anyExtendedKeyUsage = 0,
|
||||
id_kp_serverAuth = 1, // id-kp-serverAuth
|
||||
id_kp_clientAuth = 2, // id-kp-clientAuth
|
||||
id_kp_codeSigning = 3, // id-kp-codeSigning
|
||||
id_kp_emailProtection = 4, // id-kp-emailProtection
|
||||
id_kp_OCSPSigning = 9, // id-kp-OCSPSigning
|
||||
};
|
||||
|
||||
struct CertPolicyId {
|
||||
uint16_t numBytes;
|
||||
static const uint16_t MAX_BYTES = 24;
|
||||
uint8_t bytes[MAX_BYTES];
|
||||
|
||||
bool IsAnyPolicy() const;
|
||||
|
||||
static const CertPolicyId anyPolicy;
|
||||
};
|
||||
|
||||
MOZILLA_PKIX_ENUM_CLASS TrustLevel {
|
||||
TrustAnchor = 1, // certificate is a trusted root CA certificate or
|
||||
|
@ -65,16 +85,16 @@ public:
|
|||
// This will be called for every certificate encountered during path
|
||||
// building.
|
||||
//
|
||||
// When policy == SEC_OID_X509_ANY_POLICY, then no policy-related checking
|
||||
// should be done. When policy != SEC_OID_X509_ANY_POLICY, then GetCertTrust
|
||||
// MUST NOT return with *trustLevel == TrustAnchor unless the given cert is
|
||||
// considered a trust anchor *for that policy*. In particular, if the user
|
||||
// has marked an intermediate certificate as trusted, but that intermediate
|
||||
// isn't in the list of EV roots, then GetCertTrust must result in
|
||||
// When policy.IsAnyPolicy(), then no policy-related checking should be done.
|
||||
// When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with
|
||||
// *trustLevel == TrustAnchor unless the given cert is considered a trust
|
||||
// anchor *for that policy*. In particular, if the user has marked an
|
||||
// intermediate certificate as trusted, but that intermediate isn't in the
|
||||
// list of EV roots, then GetCertTrust must result in
|
||||
// *trustLevel == InheritsTrust instead of *trustLevel == TrustAnchor
|
||||
// (assuming the candidate cert is not actively distrusted).
|
||||
virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
SECOidTag policy,
|
||||
const CertPolicyId& policy,
|
||||
const CERTCertificate* candidateCert,
|
||||
/*out*/ TrustLevel* trustLevel) = 0;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifndef MOZILLA_PKIX_USE_REAL_FUNCTIONAL
|
||||
|
||||
#include "pkix/bind.h"
|
||||
|
||||
|
@ -32,4 +32,4 @@ Placeholder1 _1;
|
|||
|
||||
} } // namespace mozilla::pkix
|
||||
|
||||
#endif // _MSC_VER
|
||||
#endif
|
||||
|
|
|
@ -114,8 +114,8 @@ static Result BuildForward(TrustDomain& trustDomain,
|
|||
PRTime time,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
KeyUsages requiredKeyUsagesIfPresent,
|
||||
SECOidTag requiredEKUIfPresent,
|
||||
SECOidTag requiredPolicy,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const SECItem* stapledOCSPResponse,
|
||||
unsigned int subCACount,
|
||||
/*out*/ ScopedCERTCertList& results);
|
||||
|
@ -126,8 +126,8 @@ BuildForwardInner(TrustDomain& trustDomain,
|
|||
BackCert& subject,
|
||||
PRTime time,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
SECOidTag requiredEKUIfPresent,
|
||||
SECOidTag requiredPolicy,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
CERTCertificate* potentialIssuerCertToDup,
|
||||
unsigned int subCACount,
|
||||
ScopedCERTCertList& results)
|
||||
|
@ -196,8 +196,8 @@ BuildForward(TrustDomain& trustDomain,
|
|||
PRTime time,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
KeyUsages requiredKeyUsagesIfPresent,
|
||||
SECOidTag requiredEKUIfPresent,
|
||||
SECOidTag requiredPolicy,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const SECItem* stapledOCSPResponse,
|
||||
unsigned int subCACount,
|
||||
/*out*/ ScopedCERTCertList& results)
|
||||
|
@ -336,8 +336,8 @@ BuildCertChain(TrustDomain& trustDomain,
|
|||
PRTime time,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
/*optional*/ KeyUsages requiredKeyUsagesIfPresent,
|
||||
/*optional*/ SECOidTag requiredEKUIfPresent,
|
||||
/*optional*/ SECOidTag requiredPolicy,
|
||||
/*optional*/ KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*optional*/ const SECItem* stapledOCSPResponse,
|
||||
/*out*/ ScopedCERTCertList& results)
|
||||
{
|
||||
|
@ -355,7 +355,7 @@ BuildCertChain(TrustDomain& trustDomain,
|
|||
// domain name the certificate is valid for.
|
||||
BackCert::IncludeCN includeCN
|
||||
= endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
|
||||
requiredEKUIfPresent == SEC_OID_EXT_KEY_USAGE_SERVER_AUTH
|
||||
requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth
|
||||
? BackCert::IncludeCN::Yes
|
||||
: BackCert::IncludeCN::No;
|
||||
|
||||
|
|
|
@ -24,11 +24,11 @@
|
|||
|
||||
#include <limits>
|
||||
|
||||
#include "pkix/bind.h"
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkixcheck.h"
|
||||
#include "pkixder.h"
|
||||
#include "pkixutil.h"
|
||||
#include "secder.h"
|
||||
|
||||
namespace mozilla { namespace pkix {
|
||||
|
||||
|
@ -107,28 +107,80 @@ CheckKeyUsage(EndEntityOrCA endEntityOrCA,
|
|||
}
|
||||
|
||||
// RFC5820 4.2.1.4. Certificate Policies
|
||||
//
|
||||
|
||||
// "The user-initial-policy-set contains the special value any-policy if the
|
||||
// user is not concerned about certificate policy."
|
||||
Result
|
||||
CheckCertificatePolicies(BackCert& cert, EndEntityOrCA endEntityOrCA,
|
||||
bool isTrustAnchor, SECOidTag requiredPolicy)
|
||||
//
|
||||
// id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29}
|
||||
// id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
|
||||
// anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 }
|
||||
|
||||
/*static*/ const CertPolicyId CertPolicyId::anyPolicy = {
|
||||
4, { (40*2)+5, 29, 32, 0 }
|
||||
};
|
||||
|
||||
bool CertPolicyId::IsAnyPolicy() const
|
||||
{
|
||||
if (requiredPolicy == SEC_OID_X509_ANY_POLICY) {
|
||||
return Success;
|
||||
return this == &anyPolicy ||
|
||||
(numBytes == anyPolicy.numBytes &&
|
||||
!memcmp(bytes, anyPolicy.bytes, anyPolicy.numBytes));
|
||||
}
|
||||
|
||||
// PolicyInformation ::= SEQUENCE {
|
||||
// policyIdentifier CertPolicyId,
|
||||
// policyQualifiers SEQUENCE SIZE (1..MAX) OF
|
||||
// PolicyQualifierInfo OPTIONAL }
|
||||
inline der::Result
|
||||
CheckPolicyInformation(der::Input& input, EndEntityOrCA endEntityOrCA,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
/*in/out*/ bool& found)
|
||||
{
|
||||
if (input.MatchTLV(der::OIDTag, requiredPolicy.numBytes,
|
||||
requiredPolicy.bytes)) {
|
||||
found = true;
|
||||
} else if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
|
||||
input.MatchTLV(der::OIDTag, CertPolicyId::anyPolicy.numBytes,
|
||||
CertPolicyId::anyPolicy.bytes)) {
|
||||
found = true;
|
||||
}
|
||||
|
||||
// It is likely some callers will pass SEC_OID_UNKNOWN when they don't care,
|
||||
// instead of passing SEC_OID_X509_ANY_POLICY. Help them out by failing hard.
|
||||
if (requiredPolicy == SEC_OID_UNKNOWN) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return FatalError;
|
||||
// RFC 5280 Section 4.2.1.4 says "Optional qualifiers, which MAY be present,
|
||||
// are not expected to change the definition of the policy." Also, it seems
|
||||
// that Section 6, which defines validation, does not require any matching of
|
||||
// qualifiers. Thus, doing anything with the policy qualifiers would be a
|
||||
// waste of time and a source of potential incompatibilities, so we just
|
||||
// ignore them.
|
||||
|
||||
// Skip unmatched OID and/or policyQualifiers
|
||||
input.SkipToEnd();
|
||||
|
||||
return der::Success;
|
||||
}
|
||||
|
||||
// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
|
||||
Result
|
||||
CheckCertificatePolicies(EndEntityOrCA endEntityOrCA,
|
||||
const SECItem* encodedCertificatePolicies,
|
||||
const SECItem* encodedInhibitAnyPolicy,
|
||||
TrustLevel trustLevel,
|
||||
const CertPolicyId& requiredPolicy)
|
||||
{
|
||||
if (requiredPolicy.numBytes == 0 ||
|
||||
requiredPolicy.numBytes > sizeof requiredPolicy.bytes) {
|
||||
return Fail(FatalError, SEC_ERROR_INVALID_ARGS);
|
||||
}
|
||||
|
||||
// Ignore all policy information if the caller indicates any policy is
|
||||
// acceptable. See TrustDomain::GetCertTrust and the policy part of
|
||||
// BuildCertChain's documentation.
|
||||
if (requiredPolicy.IsAnyPolicy()) {
|
||||
return Success;
|
||||
}
|
||||
|
||||
// Bug 989051. Until we handle inhibitAnyPolicy we will fail close when
|
||||
// inhibitAnyPolicy extension is present and we need to evaluate certificate
|
||||
// policies.
|
||||
if (cert.encodedInhibitAnyPolicy) {
|
||||
if (encodedInhibitAnyPolicy) {
|
||||
return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
|
||||
}
|
||||
|
||||
|
@ -136,62 +188,46 @@ CheckCertificatePolicies(BackCert& cert, EndEntityOrCA endEntityOrCA,
|
|||
// trusted for, so we cannot require the policies to be present in those
|
||||
// certificates. Instead, the determination of which roots are trusted for
|
||||
// which policies is made by the TrustDomain's GetCertTrust method.
|
||||
if (isTrustAnchor && endEntityOrCA == EndEntityOrCA::MustBeCA) {
|
||||
if (trustLevel == TrustLevel::TrustAnchor &&
|
||||
endEntityOrCA == EndEntityOrCA::MustBeCA) {
|
||||
return Success;
|
||||
}
|
||||
|
||||
if (!cert.encodedCertificatePolicies) {
|
||||
if (!encodedCertificatePolicies) {
|
||||
return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
|
||||
}
|
||||
|
||||
ScopedPtr<CERTCertificatePolicies, CERT_DestroyCertificatePoliciesExtension>
|
||||
policies(CERT_DecodeCertificatePoliciesExtension(
|
||||
cert.encodedCertificatePolicies));
|
||||
if (!policies) {
|
||||
return MapSECStatus(SECFailure);
|
||||
}
|
||||
|
||||
for (const CERTPolicyInfo* const* policyInfos = policies->policyInfos;
|
||||
*policyInfos; ++policyInfos) {
|
||||
if ((*policyInfos)->oid == requiredPolicy) {
|
||||
return Success;
|
||||
}
|
||||
// Intermediate certs are allowed to have the anyPolicy OID
|
||||
if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
|
||||
(*policyInfos)->oid == SEC_OID_X509_ANY_POLICY) {
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
bool found = false;
|
||||
|
||||
der::Input input;
|
||||
if (input.Init(encodedCertificatePolicies->data,
|
||||
encodedCertificatePolicies->len) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
|
||||
}
|
||||
if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::EmptyAllowed::No,
|
||||
bind(CheckPolicyInformation, _1, endEntityOrCA,
|
||||
requiredPolicy, ref(found))) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
|
||||
}
|
||||
if (der::End(input) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
|
||||
}
|
||||
if (!found) {
|
||||
return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static const long UNLIMITED_PATH_LEN = -1; // must be less than zero
|
||||
|
||||
// BasicConstraints ::= SEQUENCE {
|
||||
// cA BOOLEAN DEFAULT FALSE,
|
||||
// pathLenConstraint INTEGER (0..MAX) OPTIONAL }
|
||||
der::Result
|
||||
DecodeBasicConstraints(const SECItem* encodedBasicConstraints,
|
||||
CERTBasicConstraints& basicConstraints)
|
||||
static der::Result
|
||||
DecodeBasicConstraints(der::Input& input, /*out*/ bool& isCA,
|
||||
/*out*/ long& pathLenConstraint)
|
||||
{
|
||||
PR_ASSERT(encodedBasicConstraints);
|
||||
if (!encodedBasicConstraints) {
|
||||
return der::Fail(SEC_ERROR_INVALID_ARGS);
|
||||
}
|
||||
|
||||
basicConstraints.isCA = false;
|
||||
basicConstraints.pathLenConstraint = 0;
|
||||
|
||||
der::Input input;
|
||||
if (input.Init(encodedBasicConstraints->data, encodedBasicConstraints->len)
|
||||
!= der::Success) {
|
||||
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
|
||||
if (der::ExpectTagAndIgnoreLength(input, der::SEQUENCE) != der::Success) {
|
||||
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
|
||||
bool isCA = false;
|
||||
// TODO(bug 989518): cA is by default false. According to DER, default
|
||||
// values must not be explicitly encoded in a SEQUENCE. So, if this
|
||||
// value is present and false, it is an encoding error. However, Go Daddy
|
||||
|
@ -201,51 +237,43 @@ DecodeBasicConstraints(const SECItem* encodedBasicConstraints,
|
|||
if (der::OptionalBoolean(input, true, isCA) != der::Success) {
|
||||
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
basicConstraints.isCA = isCA;
|
||||
|
||||
if (input.Peek(der::INTEGER)) {
|
||||
SECItem pathLenConstraintEncoded;
|
||||
if (der::Integer(input, pathLenConstraintEncoded) != der::Success) {
|
||||
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
long pathLenConstraint = DER_GetInteger(&pathLenConstraintEncoded);
|
||||
if (pathLenConstraint >= std::numeric_limits<int>::max() ||
|
||||
pathLenConstraint < 0) {
|
||||
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
basicConstraints.pathLenConstraint = static_cast<int>(pathLenConstraint);
|
||||
// TODO(bug 985025): If isCA is false, pathLenConstraint MUST NOT
|
||||
// be included (as per RFC 5280 section 4.2.1.9), but for compatibility
|
||||
// reasons, we don't check this for now.
|
||||
} else if (basicConstraints.isCA) {
|
||||
// If this is a CA but the path length is omitted, it is unlimited.
|
||||
basicConstraints.pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT;
|
||||
}
|
||||
|
||||
if (der::End(input) != der::Success) {
|
||||
if (OptionalInteger(input, UNLIMITED_PATH_LEN, pathLenConstraint)
|
||||
!= der::Success) {
|
||||
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
|
||||
return der::Success;
|
||||
}
|
||||
|
||||
// RFC5280 4.2.1.9. Basic Constraints (id-ce-basicConstraints)
|
||||
Result
|
||||
CheckBasicConstraints(const BackCert& cert,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
bool isTrustAnchor,
|
||||
CheckBasicConstraints(EndEntityOrCA endEntityOrCA,
|
||||
const SECItem* encodedBasicConstraints,
|
||||
const der::Version version, TrustLevel trustLevel,
|
||||
unsigned int subCACount)
|
||||
{
|
||||
CERTBasicConstraints basicConstraints;
|
||||
if (cert.encodedBasicConstraints) {
|
||||
if (DecodeBasicConstraints(cert.encodedBasicConstraints,
|
||||
basicConstraints) != der::Success) {
|
||||
return RecoverableError;
|
||||
bool isCA = false;
|
||||
long pathLenConstraint = UNLIMITED_PATH_LEN;
|
||||
|
||||
if (encodedBasicConstraints) {
|
||||
der::Input input;
|
||||
if (input.Init(encodedBasicConstraints->data,
|
||||
encodedBasicConstraints->len) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
if (der::Nested(input, der::SEQUENCE,
|
||||
bind(DecodeBasicConstraints, _1, ref(isCA),
|
||||
ref(pathLenConstraint))) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
if (der::End(input) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
|
||||
}
|
||||
} else {
|
||||
// Synthesize a non-CA basic constraints by default
|
||||
basicConstraints.isCA = false;
|
||||
basicConstraints.pathLenConstraint = 0;
|
||||
|
||||
// "If the basic constraints extension is not present in a version 3
|
||||
// certificate, or the extension is present but the cA boolean is not
|
||||
// asserted, then the certified public key MUST NOT be used to verify
|
||||
|
@ -255,22 +283,16 @@ CheckBasicConstraints(const BackCert& cert,
|
|||
// constraints as CAs.
|
||||
//
|
||||
// TODO: add check for self-signedness?
|
||||
if (endEntityOrCA == EndEntityOrCA::MustBeCA && isTrustAnchor) {
|
||||
const CERTCertificate* nssCert = cert.GetNSSCert();
|
||||
// We only allow trust anchor CA certs to omit the
|
||||
// basicConstraints extension if they are v1. v1 is encoded
|
||||
// implicitly.
|
||||
if (!nssCert->version.data && !nssCert->version.len) {
|
||||
basicConstraints.isCA = true;
|
||||
basicConstraints.pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT;
|
||||
}
|
||||
if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
|
||||
trustLevel == TrustLevel::TrustAnchor && version == der::Version::v1) {
|
||||
isCA = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
|
||||
// CA certificates are not trusted as EE certs.
|
||||
|
||||
if (basicConstraints.isCA) {
|
||||
if (isCA) {
|
||||
// XXX: We use SEC_ERROR_CA_CERT_INVALID here so we can distinguish
|
||||
// this error from other errors, given that NSS does not have a "CA cert
|
||||
// used as end-entity" error code since it doesn't have such a
|
||||
|
@ -290,16 +312,14 @@ CheckBasicConstraints(const BackCert& cert,
|
|||
PORT_Assert(endEntityOrCA == EndEntityOrCA::MustBeCA);
|
||||
|
||||
// End-entity certificates are not allowed to act as CA certs.
|
||||
if (!basicConstraints.isCA) {
|
||||
if (!isCA) {
|
||||
return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID);
|
||||
}
|
||||
|
||||
if (basicConstraints.pathLenConstraint >= 0) {
|
||||
if (subCACount >
|
||||
static_cast<unsigned int>(basicConstraints.pathLenConstraint)) {
|
||||
if (pathLenConstraint >= 0 &&
|
||||
static_cast<long>(subCACount) > pathLenConstraint) {
|
||||
return Fail(RecoverableError, SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
@ -368,15 +388,97 @@ CheckNameConstraints(BackCert& cert)
|
|||
}
|
||||
|
||||
// 4.2.1.12. Extended Key Usage (id-ce-extKeyUsage)
|
||||
// 4.2.1.12. Extended Key Usage (id-ce-extKeyUsage)
|
||||
Result
|
||||
CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedEKUs,
|
||||
SECOidTag requiredEKU)
|
||||
{
|
||||
// TODO: Either do not allow anyExtendedKeyUsage to be passed as requiredEKU,
|
||||
// or require that callers pass anyExtendedKeyUsage instead of
|
||||
// SEC_OID_UNKNWON and disallow SEC_OID_UNKNWON.
|
||||
|
||||
static der::Result
|
||||
MatchEKU(der::Input& value, KeyPurposeId requiredEKU,
|
||||
EndEntityOrCA endEntityOrCA, /*in/out*/ bool& found,
|
||||
/*in/out*/ bool& foundOCSPSigning)
|
||||
{
|
||||
// See Section 5.9 of "A Layman's Guide to a Subset of ASN.1, BER, and DER"
|
||||
// for a description of ASN.1 DER encoding of OIDs.
|
||||
|
||||
// id-pkix OBJECT IDENTIFIER ::=
|
||||
// { iso(1) identified-organization(3) dod(6) internet(1)
|
||||
// security(5) mechanisms(5) pkix(7) }
|
||||
// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
|
||||
// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
|
||||
// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
|
||||
// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
|
||||
// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
|
||||
// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
|
||||
static const uint8_t server[] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 1 };
|
||||
static const uint8_t client[] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 2 };
|
||||
static const uint8_t code [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 3 };
|
||||
static const uint8_t email [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 4 };
|
||||
static const uint8_t ocsp [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 9 };
|
||||
|
||||
// id-Netscape OBJECT IDENTIFIER ::= { 2 16 840 1 113730 }
|
||||
// id-Netscape-policy OBJECT IDENTIFIER ::= { id-Netscape 4 }
|
||||
// id-Netscape-stepUp OBJECT IDENTIFIER ::= { id-Netscape-policy 1 }
|
||||
static const uint8_t serverStepUp[] =
|
||||
{ (40*2)+16, 128+6,72, 1, 128+6,128+120,66, 4, 1 };
|
||||
|
||||
bool match = false;
|
||||
|
||||
if (!found) {
|
||||
switch (requiredEKU) {
|
||||
case KeyPurposeId::id_kp_serverAuth:
|
||||
// Treat CA certs with step-up OID as also having SSL server type.
|
||||
// Comodo has issued certificates that require this behavior that don't
|
||||
// expire until June 2020! TODO(bug 982932): Limit this exception to
|
||||
// old certificates.
|
||||
match = value.MatchBytes(server) ||
|
||||
(endEntityOrCA == EndEntityOrCA::MustBeCA &&
|
||||
value.MatchBytes(serverStepUp));
|
||||
break;
|
||||
|
||||
case KeyPurposeId::id_kp_clientAuth:
|
||||
match = value.MatchBytes(client);
|
||||
break;
|
||||
|
||||
case KeyPurposeId::id_kp_codeSigning:
|
||||
match = value.MatchBytes(code);
|
||||
break;
|
||||
|
||||
case KeyPurposeId::id_kp_emailProtection:
|
||||
match = value.MatchBytes(email);
|
||||
break;
|
||||
|
||||
case KeyPurposeId::id_kp_OCSPSigning:
|
||||
match = value.MatchBytes(ocsp);
|
||||
break;
|
||||
|
||||
case KeyPurposeId::anyExtendedKeyUsage:
|
||||
PR_NOT_REACHED("anyExtendedKeyUsage should start with found==true");
|
||||
return der::Fail(SEC_ERROR_LIBRARY_FAILURE);
|
||||
|
||||
default:
|
||||
PR_NOT_REACHED("unrecognized EKU");
|
||||
return der::Fail(SEC_ERROR_LIBRARY_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (value.AtEnd()) {
|
||||
found = true;
|
||||
if (requiredEKU == KeyPurposeId::id_kp_OCSPSigning) {
|
||||
foundOCSPSigning = true;
|
||||
}
|
||||
}
|
||||
} else if (value.MatchBytes(ocsp) && value.AtEnd()) {
|
||||
foundOCSPSigning = true;
|
||||
}
|
||||
|
||||
value.SkipToEnd(); // ignore unmatched OIDs.
|
||||
|
||||
return der::Success;
|
||||
}
|
||||
|
||||
Result
|
||||
CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
|
||||
const SECItem* encodedExtendedKeyUsage,
|
||||
KeyPurposeId requiredEKU)
|
||||
{
|
||||
// XXX: We're using SEC_ERROR_INADEQUATE_CERT_TYPE here so that callers can
|
||||
// distinguish EKU mismatch from KU mismatch from basic constraints mismatch.
|
||||
// We should probably add a new error code that is more clear for this type
|
||||
|
@ -384,35 +486,22 @@ CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedEKUs,
|
|||
|
||||
bool foundOCSPSigning = false;
|
||||
|
||||
if (encodedEKUs) {
|
||||
ScopedPtr<CERTOidSequence, CERT_DestroyOidSequence>
|
||||
seq(CERT_DecodeOidSequence(encodedEKUs));
|
||||
if (!seq) {
|
||||
PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
|
||||
return RecoverableError;
|
||||
}
|
||||
if (encodedExtendedKeyUsage) {
|
||||
bool found = requiredEKU == KeyPurposeId::anyExtendedKeyUsage;
|
||||
|
||||
bool found = false;
|
||||
|
||||
// XXX: We allow duplicate entries.
|
||||
for (const SECItem* const* oids = seq->oids; oids && *oids; ++oids) {
|
||||
SECOidTag oidTag = SECOID_FindOIDTag(*oids);
|
||||
if (requiredEKU != SEC_OID_UNKNOWN && oidTag == requiredEKU) {
|
||||
found = true;
|
||||
} else {
|
||||
// Treat CA certs with step-up OID as also having SSL server type.
|
||||
// COMODO has issued certificates that require this behavior
|
||||
// that don't expire until June 2020!
|
||||
// TODO 982932: Limit this expection to old certificates
|
||||
if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
|
||||
requiredEKU == SEC_OID_EXT_KEY_USAGE_SERVER_AUTH &&
|
||||
oidTag == SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) {
|
||||
found = true;
|
||||
der::Input input;
|
||||
if (input.Init(encodedExtendedKeyUsage->data,
|
||||
encodedExtendedKeyUsage->len) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
|
||||
}
|
||||
if (der::NestedOf(input, der::SEQUENCE, der::OIDTag, der::EmptyAllowed::No,
|
||||
bind(MatchEKU, _1, requiredEKU, endEntityOrCA,
|
||||
ref(found), ref(foundOCSPSigning)))
|
||||
!= der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
|
||||
}
|
||||
if (oidTag == SEC_OID_OCSP_RESPONDER) {
|
||||
foundOCSPSigning = true;
|
||||
}
|
||||
if (der::End(input) != der::Success) {
|
||||
return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
|
||||
}
|
||||
|
||||
// If the EKU extension was included, then the required EKU must be in the
|
||||
|
@ -437,9 +526,8 @@ CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedEKUs,
|
|||
// Allowing this exception does not cause any security issues because we
|
||||
// require delegated OCSP response signing certificates to be end-entity
|
||||
// certificates.
|
||||
if (foundOCSPSigning && requiredEKU != SEC_OID_OCSP_RESPONDER) {
|
||||
PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
|
||||
return RecoverableError;
|
||||
if (foundOCSPSigning && requiredEKU != KeyPurposeId::id_kp_OCSPSigning) {
|
||||
return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
|
||||
}
|
||||
// http://tools.ietf.org/html/rfc6960#section-4.2.2.2:
|
||||
// "OCSP signing delegation SHALL be designated by the inclusion of
|
||||
|
@ -450,9 +538,8 @@ CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedEKUs,
|
|||
// EKU extension is missing from an end-entity certificate. However, any CA
|
||||
// certificate can issue a delegated OCSP response signing certificate, so
|
||||
// we can't require the EKU be explicitly included for CA certificates.
|
||||
if (!foundOCSPSigning && requiredEKU == SEC_OID_OCSP_RESPONDER) {
|
||||
PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
|
||||
return RecoverableError;
|
||||
if (!foundOCSPSigning && requiredEKU == KeyPurposeId::id_kp_OCSPSigning) {
|
||||
return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,8 +552,8 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
|
|||
PRTime time,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
KeyUsages requiredKeyUsagesIfPresent,
|
||||
SECOidTag requiredEKUIfPresent,
|
||||
SECOidTag requiredPolicy,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
unsigned int subCACount,
|
||||
/*optional out*/ TrustLevel* trustLevelOut)
|
||||
{
|
||||
|
@ -493,8 +580,13 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
|
|||
*trustLevelOut = trustLevel;
|
||||
}
|
||||
|
||||
bool isTrustAnchor = endEntityOrCA == EndEntityOrCA::MustBeCA &&
|
||||
trustLevel == TrustLevel::TrustAnchor;
|
||||
// XXX: Good enough for now. There could be an illegal explicit version
|
||||
// number or one we don't support, but we can safely treat those all as v3
|
||||
// for now since processing of v3 certificates is strictly more strict than
|
||||
// processing of v1 certificates.
|
||||
der::Version version = (!cert.GetNSSCert()->version.data &&
|
||||
!cert.GetNSSCert()->version.len) ? der::Version::v1
|
||||
: der::Version::v3;
|
||||
|
||||
PLArenaPool* arena = cert.GetArena();
|
||||
if (!arena) {
|
||||
|
@ -513,7 +605,8 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
|
|||
}
|
||||
|
||||
// 4.2.1.4. Certificate Policies
|
||||
rv = CheckCertificatePolicies(cert, endEntityOrCA, isTrustAnchor,
|
||||
rv = CheckCertificatePolicies(endEntityOrCA, cert.encodedCertificatePolicies,
|
||||
cert.encodedInhibitAnyPolicy, trustLevel,
|
||||
requiredPolicy);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
|
@ -531,7 +624,8 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
|
|||
// checking.
|
||||
|
||||
// 4.2.1.9. Basic Constraints.
|
||||
rv = CheckBasicConstraints(cert, endEntityOrCA, isTrustAnchor, subCACount);
|
||||
rv = CheckBasicConstraints(endEntityOrCA, cert.encodedBasicConstraints,
|
||||
version, trustLevel, subCACount);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#ifndef mozilla_pkix__pkixcheck_h
|
||||
#define mozilla_pkix__pkixcheck_h
|
||||
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "pkixutil.h"
|
||||
#include "certt.h"
|
||||
|
||||
|
@ -36,8 +37,8 @@ Result CheckIssuerIndependentProperties(
|
|||
PRTime time,
|
||||
EndEntityOrCA endEntityOrCA,
|
||||
KeyUsages requiredKeyUsagesIfPresent,
|
||||
SECOidTag requiredEKUIfPresent,
|
||||
SECOidTag requiredPolicy,
|
||||
KeyPurposeId requiredEKUIfPresent,
|
||||
const CertPolicyId& requiredPolicy,
|
||||
unsigned int subCACount,
|
||||
/*optional out*/ TrustLevel* trustLevel = nullptr);
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ Fail(PRErrorCode errorCode)
|
|||
return Failure;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Too complicated to be inline
|
||||
Result
|
||||
ExpectTagAndGetLength(Input& input, uint8_t expectedTag, uint16_t& length)
|
||||
|
@ -86,4 +88,6 @@ ExpectTagAndGetLength(Input& input, uint8_t expectedTag, uint16_t& length)
|
|||
return input.EnsureLength(length);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} } } // namespace mozilla::pkix::der
|
||||
|
|
|
@ -25,6 +25,18 @@
|
|||
#ifndef mozilla_pkix__pkixder_h
|
||||
#define mozilla_pkix__pkixder_h
|
||||
|
||||
// Expect* functions advance the input mark and return Success if the input
|
||||
// matches the given criteria; they return Failure with the input mark in an
|
||||
// undefined state if the input does not match the criteria.
|
||||
//
|
||||
// Match* functions advance the input mark and return true if the input matches
|
||||
// the given criteria; they return false without changing the input mark if the
|
||||
// input does not match the criteria.
|
||||
//
|
||||
// Skip* functions unconditionally advance the input mark and return Success if
|
||||
// they are able to do so; otherwise they return Failure with the input mark in
|
||||
// an undefined state.
|
||||
|
||||
#include "pkix/enumclass.h"
|
||||
#include "pkix/nullptr.h"
|
||||
|
||||
|
@ -138,6 +150,44 @@ public:
|
|||
return Success;
|
||||
}
|
||||
|
||||
template <uint16_t N>
|
||||
bool MatchBytes(const uint8_t (&toMatch)[N])
|
||||
{
|
||||
if (EnsureLength(N) != Success) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(input, toMatch, N)) {
|
||||
return false;
|
||||
}
|
||||
input += N;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <uint16_t N>
|
||||
bool MatchTLV(uint8_t tag, uint16_t len, const uint8_t (&value)[N])
|
||||
{
|
||||
static_assert(N <= 127, "buffer larger than largest length supported");
|
||||
if (len > N) {
|
||||
PR_NOT_REACHED("overflow prevented dynamically instead of statically");
|
||||
return false;
|
||||
}
|
||||
uint16_t totalLen = 2u + len;
|
||||
if (EnsureLength(totalLen) != Success) {
|
||||
return false;
|
||||
}
|
||||
if (*input != tag) {
|
||||
return false;
|
||||
}
|
||||
if (*(input + 1) != len) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(input + 2, value, len)) {
|
||||
return false;
|
||||
}
|
||||
input += totalLen;
|
||||
return true;
|
||||
}
|
||||
|
||||
Result Skip(uint16_t len)
|
||||
{
|
||||
if (EnsureLength(len) != Success) {
|
||||
|
@ -239,14 +289,48 @@ ExpectTagAndLength(Input& input, uint8_t expectedTag, uint8_t expectedLength)
|
|||
return Success;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
Result
|
||||
ExpectTagAndGetLength(Input& input, uint8_t expectedTag, uint16_t& length);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
inline Result
|
||||
ExpectTagAndIgnoreLength(Input& input, uint8_t expectedTag)
|
||||
ExpectTagAndSkipLength(Input& input, uint8_t expectedTag)
|
||||
{
|
||||
uint16_t ignored;
|
||||
return ExpectTagAndGetLength(input, expectedTag, ignored);
|
||||
return internal::ExpectTagAndGetLength(input, expectedTag, ignored);
|
||||
}
|
||||
|
||||
inline Result
|
||||
ExpectTagAndSkipValue(Input& input, uint8_t tag)
|
||||
{
|
||||
uint16_t length;
|
||||
if (internal::ExpectTagAndGetLength(input, tag, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
return input.Skip(length);
|
||||
}
|
||||
|
||||
inline Result
|
||||
ExpectTagAndGetValue(Input& input, uint8_t tag, /*out*/ SECItem& value)
|
||||
{
|
||||
uint16_t length;
|
||||
if (internal::ExpectTagAndGetLength(input, tag, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
return input.Skip(length, value);
|
||||
}
|
||||
|
||||
inline Result
|
||||
ExpectTagAndGetValue(Input& input, uint8_t tag, /*out*/ Input& value)
|
||||
{
|
||||
uint16_t length;
|
||||
if (internal::ExpectTagAndGetLength(input, tag, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
return input.Skip(length, value);
|
||||
}
|
||||
|
||||
inline Result
|
||||
|
@ -263,20 +347,13 @@ template <typename Decoder>
|
|||
inline Result
|
||||
Nested(Input& input, uint8_t tag, Decoder decoder)
|
||||
{
|
||||
uint16_t length;
|
||||
if (ExpectTagAndGetLength(input, tag, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
Input nested;
|
||||
if (input.Skip(length, nested) != Success) {
|
||||
if (ExpectTagAndGetValue(input, tag, nested) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
if (decoder(nested) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
return End(nested);
|
||||
}
|
||||
|
||||
|
@ -287,18 +364,13 @@ Nested(Input& input, uint8_t outerTag, uint8_t innerTag, Decoder decoder)
|
|||
// XXX: This doesn't work (in VS2010):
|
||||
// return Nested(input, outerTag, bind(Nested, _1, innerTag, decoder));
|
||||
|
||||
uint16_t length;
|
||||
if (ExpectTagAndGetLength(input, outerTag, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
Input nestedInput;
|
||||
if (input.Skip(length, nestedInput) != Success) {
|
||||
if (ExpectTagAndGetValue(input, outerTag, nestedInput) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
if (Nested(nestedInput, innerTag, decoder) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
return End(nestedInput);
|
||||
}
|
||||
|
||||
|
@ -324,13 +396,8 @@ inline Result
|
|||
NestedOf(Input& input, uint8_t outerTag, uint8_t innerTag,
|
||||
EmptyAllowed mayBeEmpty, Decoder decoder)
|
||||
{
|
||||
uint16_t responsesLength;
|
||||
if (ExpectTagAndGetLength(input, outerTag, responsesLength) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
Input inner;
|
||||
if (input.Skip(responsesLength, inner) != Success) {
|
||||
if (ExpectTagAndGetValue(input, outerTag, inner) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
|
@ -350,28 +417,34 @@ NestedOf(Input& input, uint8_t outerTag, uint8_t innerTag,
|
|||
return Success;
|
||||
}
|
||||
|
||||
inline Result
|
||||
Skip(Input& input, uint8_t tag)
|
||||
{
|
||||
uint16_t length;
|
||||
if (ExpectTagAndGetLength(input, tag, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
return input.Skip(length);
|
||||
}
|
||||
|
||||
inline Result
|
||||
Skip(Input& input, uint8_t tag, /*out*/ SECItem& value)
|
||||
{
|
||||
uint16_t length;
|
||||
if (ExpectTagAndGetLength(input, tag, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
return input.Skip(length, value);
|
||||
}
|
||||
|
||||
// Universal types
|
||||
|
||||
namespace internal {
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed.
|
||||
template <typename T> inline Result
|
||||
IntegralValue(Input& input, uint8_t tag, T& value)
|
||||
{
|
||||
// Conveniently, all the Integers that we actually have to be able to parse
|
||||
// are positive and very small. Consequently, this parser is *much* simpler
|
||||
// than a general Integer parser would need to be.
|
||||
if (ExpectTagAndLength(input, tag, 1) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
uint8_t valueByte;
|
||||
if (input.Read(valueByte) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
if (valueByte & 0x80) { // negative
|
||||
return Fail(SEC_ERROR_BAD_DER);
|
||||
}
|
||||
value = valueByte;
|
||||
return Success;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
inline Result
|
||||
Boolean(Input& input, /*out*/ bool& value)
|
||||
{
|
||||
|
@ -411,61 +484,61 @@ OptionalBoolean(Input& input, bool allowInvalidExplicitEncoding,
|
|||
return Success;
|
||||
}
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed.
|
||||
inline Result
|
||||
Enumerated(Input& input, uint8_t& value)
|
||||
{
|
||||
if (ExpectTagAndLength(input, ENUMERATED | 0, 1) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
return input.Read(value);
|
||||
return internal::IntegralValue(input, ENUMERATED | 0, value);
|
||||
}
|
||||
|
||||
inline Result
|
||||
GeneralizedTime(Input& input, PRTime& time)
|
||||
{
|
||||
uint16_t length;
|
||||
SECItem encoded;
|
||||
if (ExpectTagAndGetLength(input, GENERALIZED_TIME, length) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
if (input.Skip(length, encoded) != Success) {
|
||||
if (ExpectTagAndGetValue(input, GENERALIZED_TIME, encoded) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
if (DER_GeneralizedTimeToTime(&time, &encoded) != SECSuccess) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed.
|
||||
inline Result
|
||||
Integer(Input& input, /*out*/ SECItem& value)
|
||||
Integer(Input& input, /*out*/ uint8_t& value)
|
||||
{
|
||||
uint16_t length;
|
||||
if (ExpectTagAndGetLength(input, INTEGER, length) != Success) {
|
||||
if (internal::IntegralValue(input, INTEGER, value) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
if (input.Skip(length, value) != Success) {
|
||||
// This parser will only parse values between 0..127. If this range is
|
||||
// increased then callers will need to be changed. The default value must be
|
||||
// -1; defaultValue is only a parameter to make it clear in the calling code
|
||||
// what the default value is.
|
||||
inline Result
|
||||
OptionalInteger(Input& input, long defaultValue, /*out*/ long& value)
|
||||
{
|
||||
// If we need to support a different default value in the future, we need to
|
||||
// test that parsedValue != defaultValue.
|
||||
if (defaultValue != -1) {
|
||||
return Fail(SEC_ERROR_INVALID_ARGS);
|
||||
}
|
||||
|
||||
if (!input.Peek(INTEGER)) {
|
||||
value = defaultValue;
|
||||
return Success;
|
||||
}
|
||||
|
||||
uint8_t parsedValue;
|
||||
if (Integer(input, parsedValue) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
if (value.len == 0) {
|
||||
return Fail(SEC_ERROR_BAD_DER);
|
||||
}
|
||||
|
||||
// Check for overly-long encodings. If the first byte is 0x00 then the high
|
||||
// bit on the second byte must be 1; otherwise the same *positive* value
|
||||
// could be encoded without the leading 0x00 byte. If the first byte is 0xFF
|
||||
// then the second byte must NOT have its high bit set; otherwise the same
|
||||
// *negative* value could be encoded without the leading 0xFF byte.
|
||||
if (value.len > 1) {
|
||||
if ((value.data[0] == 0x00 && (value.data[1] & 0x80) == 0) ||
|
||||
(value.data[0] == 0xff && (value.data[1] & 0x80) != 0)) {
|
||||
return Fail(SEC_ERROR_BAD_DER);
|
||||
}
|
||||
}
|
||||
|
||||
value = parsedValue;
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
@ -494,7 +567,7 @@ OID(Input& input, const uint8_t (&expectedOid)[Len])
|
|||
inline Result
|
||||
AlgorithmIdentifier(Input& input, SECAlgorithmID& algorithmID)
|
||||
{
|
||||
if (Skip(input, OIDTag, algorithmID.algorithm) != Success) {
|
||||
if (ExpectTagAndGetValue(input, OIDTag, algorithmID.algorithm) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
algorithmID.parameters.data = nullptr;
|
||||
|
@ -506,7 +579,7 @@ AlgorithmIdentifier(Input& input, SECAlgorithmID& algorithmID)
|
|||
}
|
||||
|
||||
inline Result
|
||||
CertificateSerialNumber(Input& input, /*out*/ SECItem& serialNumber)
|
||||
CertificateSerialNumber(Input& input, /*out*/ SECItem& value)
|
||||
{
|
||||
// http://tools.ietf.org/html/rfc5280#section-4.1.2.2:
|
||||
//
|
||||
|
@ -519,7 +592,27 @@ CertificateSerialNumber(Input& input, /*out*/ SECItem& serialNumber)
|
|||
// that are negative or zero. Certificate users SHOULD be prepared to
|
||||
// gracefully handle such certificates."
|
||||
|
||||
return Integer(input, serialNumber);
|
||||
if (ExpectTagAndGetValue(input, INTEGER, value) != Success) {
|
||||
return Failure;
|
||||
}
|
||||
|
||||
if (value.len == 0) {
|
||||
return Fail(SEC_ERROR_BAD_DER);
|
||||
}
|
||||
|
||||
// Check for overly-long encodings. If the first byte is 0x00 then the high
|
||||
// bit on the second byte must be 1; otherwise the same *positive* value
|
||||
// could be encoded without the leading 0x00 byte. If the first byte is 0xFF
|
||||
// then the second byte must NOT have its high bit set; otherwise the same
|
||||
// *negative* value could be encoded without the leading 0xFF byte.
|
||||
if (value.len > 1) {
|
||||
if ((value.data[0] == 0x00 && (value.data[1] & 0x80) == 0) ||
|
||||
(value.data[0] == 0xff && (value.data[1] & 0x80) != 0)) {
|
||||
return Fail(SEC_ERROR_BAD_DER);
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
// x.509 and OCSP both use this same version numbering scheme, though OCSP
|
||||
|
|
|
@ -146,8 +146,8 @@ CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
|
|||
// are validating for should be passed to CheckIssuerIndependentProperties.
|
||||
rv = CheckIssuerIndependentProperties(trustDomain, cert, time,
|
||||
EndEntityOrCA::MustBeEndEntity, 0,
|
||||
SEC_OID_OCSP_RESPONDER,
|
||||
SEC_OID_X509_ANY_POLICY, 0);
|
||||
KeyPurposeId::id_kp_OCSPSigning,
|
||||
CertPolicyId::anyPolicy, 0);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -267,7 +267,8 @@ GetOCSPSignerCertificate(TrustDomain& trustDomain,
|
|||
return nullptr;
|
||||
}
|
||||
SECItem keyHash;
|
||||
if (der::Skip(responderID, der::OCTET_STRING, keyHash) != der::Success) {
|
||||
if (der::ExpectTagAndGetValue(responderID, der::OCTET_STRING, keyHash)
|
||||
!= der::Success) {
|
||||
return nullptr;
|
||||
}
|
||||
if (MatchKeyHash(keyHash, *potentialSigner.get(), match) != der::Success) {
|
||||
|
@ -448,20 +449,14 @@ BasicResponse(der::Input& input, Context& context)
|
|||
{
|
||||
der::Input::Mark mark(input.GetMark());
|
||||
|
||||
uint16_t length;
|
||||
if (der::ExpectTagAndGetLength(input, der::SEQUENCE, length)
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
// The signature covers the entire DER encoding of tbsResponseData, including
|
||||
// the beginning tag and length. However, when we're parsing tbsResponseData,
|
||||
// we want to strip off the tag and length because we don't need it after
|
||||
// we've confirmed it's there and figured out what length it is.
|
||||
|
||||
der::Input tbsResponseData;
|
||||
|
||||
if (input.Skip(length, tbsResponseData) != der::Success) {
|
||||
if (der::ExpectTagAndGetValue(input, der::SEQUENCE, tbsResponseData)
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
|
@ -477,7 +472,8 @@ BasicResponse(der::Input& input, Context& context)
|
|||
return der::Failure;
|
||||
}
|
||||
|
||||
if (der::Skip(input, der::BIT_STRING, signedData.signature) != der::Success) {
|
||||
if (der::ExpectTagAndGetValue(input, der::BIT_STRING, signedData.signature)
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
if (signedData.signature.len == 0) {
|
||||
|
@ -506,14 +502,14 @@ BasicResponse(der::Input& input, Context& context)
|
|||
// and too long and we'll have leftover data that won't parse as a cert.
|
||||
|
||||
// [0] wrapper
|
||||
if (der::ExpectTagAndIgnoreLength(
|
||||
if (der::ExpectTagAndSkipLength(
|
||||
input, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0)
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
// SEQUENCE wrapper
|
||||
if (der::ExpectTagAndIgnoreLength(input, der::SEQUENCE) != der::Success) {
|
||||
if (der::ExpectTagAndSkipLength(input, der::SEQUENCE) != der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
|
@ -526,7 +522,7 @@ BasicResponse(der::Input& input, Context& context)
|
|||
// Unwrap the SEQUENCE that contains the certificate, which is itself a
|
||||
// SEQUENCE.
|
||||
der::Input::Mark mark(input.GetMark());
|
||||
if (der::Skip(input, der::SEQUENCE) != der::Success) {
|
||||
if (der::ExpectTagAndSkipValue(input, der::SEQUENCE) != der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
|
@ -564,18 +560,12 @@ ResponseData(der::Input& input, Context& context,
|
|||
// byName [1] Name,
|
||||
// byKey [2] KeyHash }
|
||||
SECItem responderID;
|
||||
uint16_t responderIDLength;
|
||||
ResponderIDType responderIDType
|
||||
= input.Peek(static_cast<uint8_t>(ResponderIDType::byName))
|
||||
? ResponderIDType::byName
|
||||
: ResponderIDType::byKey;
|
||||
if (ExpectTagAndGetLength(input, static_cast<uint8_t>(responderIDType),
|
||||
responderIDLength) != der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
// TODO: responderID probably needs to have another level of ASN1 tag/length
|
||||
// checked and stripped.
|
||||
if (input.Skip(responderIDLength, responderID) != der::Success) {
|
||||
if (ExpectTagAndGetValue(input, static_cast<uint8_t>(responderIDType),
|
||||
responderID) != der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
|
@ -663,7 +653,8 @@ SingleResponse(der::Input& input, Context& context)
|
|||
// parse it. TODO: We should mention issues like this in the explanation of
|
||||
// why we treat invalid OCSP responses equivalently to revoked for OCSP
|
||||
// stapling.
|
||||
if (der::Skip(input, static_cast<uint8_t>(CertStatus::Revoked))
|
||||
if (der::ExpectTagAndSkipValue(input,
|
||||
static_cast<uint8_t>(CertStatus::Revoked))
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
@ -761,12 +752,14 @@ CertID(der::Input& input, const Context& context, /*out*/ bool& match)
|
|||
}
|
||||
|
||||
SECItem issuerNameHash;
|
||||
if (der::Skip(input, der::OCTET_STRING, issuerNameHash) != der::Success) {
|
||||
if (der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerNameHash)
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
SECItem issuerKeyHash;
|
||||
if (der::Skip(input, der::OCTET_STRING, issuerKeyHash) != der::Success) {
|
||||
if (der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerKeyHash)
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
|
@ -851,13 +844,8 @@ MatchKeyHash(const SECItem& keyHash, const CERTCertificate& cert,
|
|||
static der::Result
|
||||
CheckExtensionForCriticality(der::Input& input)
|
||||
{
|
||||
uint16_t toSkip;
|
||||
if (ExpectTagAndGetLength(input, der::OIDTag, toSkip) != der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
// TODO: maybe we should check the syntax of the OID value
|
||||
if (input.Skip(toSkip) != der::Success) {
|
||||
if (ExpectTagAndSkipValue(input, der::OIDTag) != der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
|
||||
|
@ -867,11 +855,9 @@ CheckExtensionForCriticality(der::Input& input)
|
|||
return der::Fail(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
|
||||
}
|
||||
|
||||
if (ExpectTagAndGetLength(input, der::OCTET_STRING, toSkip)
|
||||
!= der::Success) {
|
||||
return der::Failure;
|
||||
}
|
||||
return input.Skip(toSkip);
|
||||
input.SkipToEnd();
|
||||
|
||||
return der::Success;
|
||||
}
|
||||
|
||||
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
|
||||
|
|
|
@ -362,31 +362,31 @@ TEST_F(pkixder_input_tests, SkipToSECItemPastEnd)
|
|||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, Skip)
|
||||
TEST_F(pkixder_input_tests, ExpectTagAndSkipValue)
|
||||
{
|
||||
Input input;
|
||||
ASSERT_EQ(Success,
|
||||
input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
|
||||
|
||||
ASSERT_EQ(Success, Skip(input, SEQUENCE));
|
||||
ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
|
||||
ASSERT_EQ(Success, End(input));
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, SkipWithTruncatedData)
|
||||
TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithTruncatedData)
|
||||
{
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
|
||||
sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
|
||||
|
||||
ASSERT_EQ(Failure, Skip(input, SEQUENCE));
|
||||
ASSERT_EQ(Failure, ExpectTagAndSkipValue(input, SEQUENCE));
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, SkipWithOverrunData)
|
||||
TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithOverrunData)
|
||||
{
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_OVERRUN_SEQUENCE_OF_INT8,
|
||||
sizeof DER_OVERRUN_SEQUENCE_OF_INT8));
|
||||
ASSERT_EQ(Success, Skip(input, SEQUENCE));
|
||||
ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
|
||||
ASSERT_EQ(Failure, End(input));
|
||||
}
|
||||
|
||||
|
@ -491,7 +491,7 @@ TEST_F(pkixder_input_tests, ExpectTagAndGetLength)
|
|||
input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
|
||||
|
||||
uint16_t length = 0;
|
||||
ASSERT_EQ(Success, ExpectTagAndGetLength(input, SEQUENCE, length));
|
||||
ASSERT_EQ(Success, internal::ExpectTagAndGetLength(input, SEQUENCE, length));
|
||||
ASSERT_EQ(sizeof DER_SEQUENCE_OF_INT8 - 2, length);
|
||||
ASSERT_EQ(Success, input.Skip(length));
|
||||
ASSERT_TRUE(input.AtEnd());
|
||||
|
@ -504,7 +504,7 @@ TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongTag)
|
|||
input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
|
||||
|
||||
uint16_t length = 0;
|
||||
ASSERT_EQ(Failure, ExpectTagAndGetLength(input, INTEGER, length));
|
||||
ASSERT_EQ(Failure, internal::ExpectTagAndGetLength(input, INTEGER, length));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
|
@ -515,23 +515,23 @@ TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongLength)
|
|||
sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
|
||||
|
||||
uint16_t length = 0;
|
||||
ASSERT_EQ(Failure, ExpectTagAndGetLength(input, SEQUENCE, length));
|
||||
ASSERT_EQ(Failure, internal::ExpectTagAndGetLength(input, SEQUENCE, length));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, ExpectTagAndIgnoreLength)
|
||||
TEST_F(pkixder_input_tests, ExpectTagAndSkipLength)
|
||||
{
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
|
||||
ASSERT_EQ(Success, ExpectTagAndIgnoreLength(input, INTEGER));
|
||||
ASSERT_EQ(Success, ExpectTagAndSkipLength(input, INTEGER));
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, ExpectTagAndIgnoreLengthWithWrongTag)
|
||||
TEST_F(pkixder_input_tests, ExpectTagAndSkipLengthWithWrongTag)
|
||||
{
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
|
||||
|
||||
ASSERT_EQ(Failure, ExpectTagAndIgnoreLength(input, OCTET_STRING));
|
||||
ASSERT_EQ(Failure, ExpectTagAndSkipLength(input, OCTET_STRING));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
|
@ -604,4 +604,57 @@ TEST_F(pkixder_input_tests, NestedOfWithTruncatedData)
|
|||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
ASSERT_EQ((size_t) 0, readValues.size());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, MatchBytesAtEnd)
|
||||
{
|
||||
Input input;
|
||||
static const uint8_t der[1] = { };
|
||||
ASSERT_EQ(Success, input.Init(der, 0));
|
||||
ASSERT_TRUE(input.AtEnd());
|
||||
static const uint8_t toMatch[] = { 1 };
|
||||
ASSERT_FALSE(input.MatchBytes(toMatch));
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, MatchBytes1Match)
|
||||
{
|
||||
Input input;
|
||||
static const uint8_t der[] = { 1 };
|
||||
ASSERT_EQ(Success, input.Init(der, sizeof der));
|
||||
ASSERT_FALSE(input.AtEnd());
|
||||
ASSERT_TRUE(input.MatchBytes(der));
|
||||
ASSERT_TRUE(input.AtEnd());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, MatchBytes1Mismatch)
|
||||
{
|
||||
Input input;
|
||||
static const uint8_t der[] = { 1 };
|
||||
ASSERT_EQ(Success, input.Init(der, sizeof der));
|
||||
static const uint8_t toMatch[] = { 2 };
|
||||
ASSERT_FALSE(input.MatchBytes(toMatch));
|
||||
ASSERT_FALSE(input.AtEnd());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, MatchBytes2Match)
|
||||
{
|
||||
Input input;
|
||||
static const uint8_t der[] = { 1, 2, 3 };
|
||||
ASSERT_EQ(Success, input.Init(der, sizeof der));
|
||||
static const uint8_t toMatch[] = { 1, 2 };
|
||||
ASSERT_TRUE(input.MatchBytes(toMatch));
|
||||
uint8_t followingByte;
|
||||
ASSERT_EQ(Success, input.Read(followingByte));
|
||||
ASSERT_EQ(3, followingByte);
|
||||
}
|
||||
|
||||
TEST_F(pkixder_input_tests, MatchBytes2Mismatch)
|
||||
{
|
||||
Input input;
|
||||
static const uint8_t der[] = { 1, 2, 3 };
|
||||
ASSERT_EQ(Success, input.Init(der, sizeof der));
|
||||
static const uint8_t toMatchMismatch[] = { 1, 3 };
|
||||
ASSERT_FALSE(input.MatchBytes(toMatchMismatch));
|
||||
ASSERT_TRUE(input.MatchBytes(der));
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
|
|
@ -79,11 +79,8 @@ TEST_F(pkixder_pki_types_tests, AlgorithmIdentifierNullParams)
|
|||
ASSERT_EQ(Success, input.Init(DER_ALGORITHM_IDENTIFIER_NULL_PARAMS,
|
||||
sizeof DER_ALGORITHM_IDENTIFIER_NULL_PARAMS));
|
||||
|
||||
uint16_t length;
|
||||
ASSERT_EQ(Success, ExpectTagAndGetLength(input, SEQUENCE, length));
|
||||
|
||||
Input nested;
|
||||
ASSERT_EQ(Success, input.Skip(length, nested));
|
||||
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, nested));
|
||||
|
||||
const uint8_t expectedAlgorithmID[] = {
|
||||
0xde, 0xad, 0xbe, 0xef
|
||||
|
|
|
@ -22,14 +22,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "pkix/bind.h"
|
||||
#include "pkixder.h"
|
||||
#include "stdint.h"
|
||||
|
||||
using namespace mozilla::pkix::der;
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -76,17 +78,17 @@ TEST_F(pkixder_universal_types_tests, BooleanTrue42)
|
|||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, BooleanTrueFF)
|
||||
{
|
||||
const uint8_t DER_BOOLEAN_TRUE_FF[] = {
|
||||
static const uint8_t DER_BOOLEAN_TRUE[] = {
|
||||
0x01, // INTEGER
|
||||
0x01, // length
|
||||
0xff // true
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, BooleanTrueFF)
|
||||
{
|
||||
Input input;
|
||||
ASSERT_EQ(Success,
|
||||
input.Init(DER_BOOLEAN_TRUE_FF, sizeof DER_BOOLEAN_TRUE_FF));
|
||||
input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
|
||||
|
||||
bool value = false;
|
||||
ASSERT_EQ(Success, Boolean(input, value));
|
||||
|
@ -265,55 +267,118 @@ TEST_F(pkixder_universal_types_tests, GeneralizedTimeInvalidZeroLength)
|
|||
ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, Integer)
|
||||
TEST_F(pkixder_universal_types_tests, Integer_0_127)
|
||||
{
|
||||
const uint8_t DER_INTEGUR[] = {
|
||||
for (uint8_t i = 0; i <= 127; ++i) {
|
||||
const uint8_t DER[] = {
|
||||
0x02, // INTEGER
|
||||
0x01, // length
|
||||
i, // value
|
||||
};
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER, sizeof DER));
|
||||
|
||||
uint8_t value = i + 1; // initialize with a value that is NOT i.
|
||||
ASSERT_EQ(Success, Integer(input, value));
|
||||
ASSERT_EQ(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, Integer_Negative1)
|
||||
{
|
||||
// This is a valid integer value but our integer parser cannot parse
|
||||
// negative values.
|
||||
|
||||
static const uint8_t DER[] = {
|
||||
0x02, // INTEGER
|
||||
0x01, // length
|
||||
0xff, // -1 (two's complement)
|
||||
};
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER, sizeof DER));
|
||||
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, Integer_Negative128)
|
||||
{
|
||||
// This is a valid integer value but our integer parser cannot parse
|
||||
// negative values.
|
||||
|
||||
static const uint8_t DER[] = {
|
||||
0x02, // INTEGER
|
||||
0x01, // length
|
||||
0x80, // -128 (two's complement)
|
||||
};
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER, sizeof DER));
|
||||
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, Integer_128)
|
||||
{
|
||||
// This is a valid integer value but our integer parser cannot parse
|
||||
// values that require more than one byte to encode.
|
||||
|
||||
static const uint8_t DER[] = {
|
||||
0x02, // INTEGER
|
||||
0x02, // length
|
||||
0x00, 0x80 // 128
|
||||
};
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER, sizeof DER));
|
||||
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, Integer11223344)
|
||||
{
|
||||
// This is a valid integer value but our integer parser cannot parse
|
||||
// values that require more than one byte to be encoded.
|
||||
|
||||
static const uint8_t DER[] = {
|
||||
0x02, // INTEGER
|
||||
0x04, // length
|
||||
0x11, 0x22, 0x33, 0x44 // 0x11223344
|
||||
};
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_INTEGUR, sizeof DER_INTEGUR));
|
||||
ASSERT_EQ(Success, input.Init(DER, sizeof DER));
|
||||
|
||||
const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33, 0x44 };
|
||||
|
||||
SECItem item;
|
||||
memset(&item, 0x00, sizeof item);
|
||||
|
||||
ASSERT_EQ(Success, Integer(input, item));
|
||||
|
||||
ASSERT_EQ(siBuffer, item.type);
|
||||
ASSERT_EQ((size_t) 4, item.len);
|
||||
ASSERT_TRUE(item.data);
|
||||
ASSERT_EQ(0, memcmp(item.data, expectedItemData, sizeof expectedItemData));
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, OneByte)
|
||||
TEST_F(pkixder_universal_types_tests, IntegerTruncatedOneByte)
|
||||
{
|
||||
const uint8_t DER_INTEGUR[] = {
|
||||
const uint8_t DER_INTEGER_TRUNCATED[] = {
|
||||
0x02, // INTEGER
|
||||
0x01, // length
|
||||
0x11 // 0x11
|
||||
// MISSING DATA HERE
|
||||
};
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_INTEGUR, sizeof DER_INTEGUR));
|
||||
ASSERT_EQ(Success,
|
||||
input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED));
|
||||
|
||||
const uint8_t expectedItemData[] = { 0x11 };
|
||||
|
||||
SECItem item;
|
||||
memset(&item, 0x00, sizeof item);
|
||||
|
||||
ASSERT_EQ(Success, Integer(input, item));
|
||||
|
||||
ASSERT_EQ(siBuffer, item.type);
|
||||
ASSERT_EQ((size_t) 1, item.len);
|
||||
ASSERT_TRUE(item.data);
|
||||
ASSERT_EQ(0, memcmp(item.data, expectedItemData, sizeof expectedItemData));
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, IntegerTruncated)
|
||||
TEST_F(pkixder_universal_types_tests, IntegerTruncatedLarge)
|
||||
{
|
||||
const uint8_t DER_INTEGER_TRUNCATED[] = {
|
||||
0x02, // INTEGER
|
||||
|
@ -326,14 +391,9 @@ TEST_F(pkixder_universal_types_tests, IntegerTruncated)
|
|||
ASSERT_EQ(Success,
|
||||
input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED));
|
||||
|
||||
SECItem item;
|
||||
memset(&item, 0x00, sizeof item);
|
||||
|
||||
ASSERT_EQ(Failure, Integer(input, item));
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
|
||||
ASSERT_EQ(0, item.type);
|
||||
ASSERT_EQ(0, item.len);
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, IntegerZeroLength)
|
||||
|
@ -346,9 +406,8 @@ TEST_F(pkixder_universal_types_tests, IntegerZeroLength)
|
|||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_INTEGER_ZERO_LENGTH,
|
||||
sizeof DER_INTEGER_ZERO_LENGTH));
|
||||
|
||||
SECItem item;
|
||||
ASSERT_EQ(Failure, Integer(input, item));
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
|
@ -363,9 +422,9 @@ TEST_F(pkixder_universal_types_tests, IntegerOverlyLong1)
|
|||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG1,
|
||||
sizeof DER_INTEGER_OVERLY_LONG1));
|
||||
|
||||
SECItem item;
|
||||
ASSERT_EQ(Failure, Integer(input, item));
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, IntegerOverlyLong2)
|
||||
|
@ -379,9 +438,60 @@ TEST_F(pkixder_universal_types_tests, IntegerOverlyLong2)
|
|||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG2,
|
||||
sizeof DER_INTEGER_OVERLY_LONG2));
|
||||
uint8_t value;
|
||||
ASSERT_EQ(Failure, Integer(input, value));
|
||||
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
|
||||
}
|
||||
|
||||
SECItem item;
|
||||
ASSERT_EQ(Failure, Integer(input, item));
|
||||
TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefault)
|
||||
{
|
||||
// The input is a BOOLEAN and not INTEGER for the input so we'll not parse
|
||||
// anything and instead use the default value.
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
|
||||
long value = 1;
|
||||
ASSERT_EQ(Success, OptionalInteger(input, -1, value));
|
||||
ASSERT_EQ(-1, value);
|
||||
bool boolValue;
|
||||
ASSERT_EQ(Success, Boolean(input, boolValue));
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, OptionalIntegerUnsupportedDefault)
|
||||
{
|
||||
// The same as the previous test, except with an unsupported default value
|
||||
// passed in.
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
|
||||
long value;
|
||||
ASSERT_EQ(Failure, OptionalInteger(input, 0, value));
|
||||
ASSERT_EQ(SEC_ERROR_INVALID_ARGS, PR_GetError());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefaultAtEnd)
|
||||
{
|
||||
static const uint8_t dummy = 1;
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(&dummy, 0));
|
||||
long value = 1;
|
||||
ASSERT_EQ(Success, OptionalInteger(input, -1, value));
|
||||
ASSERT_EQ(-1, value);
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, OptionalIntegerNonDefaultValue)
|
||||
{
|
||||
static const uint8_t DER[] = {
|
||||
0x02, // INTEGER
|
||||
0x01, // length
|
||||
0x00
|
||||
};
|
||||
|
||||
Input input;
|
||||
ASSERT_EQ(Success, input.Init(DER, sizeof DER));
|
||||
long value = 2;
|
||||
ASSERT_EQ(Success, OptionalInteger(input, -1, value));
|
||||
ASSERT_EQ(0, value);
|
||||
ASSERT_TRUE(input.AtEnd());
|
||||
}
|
||||
|
||||
TEST_F(pkixder_universal_types_tests, Null)
|
||||
|
|
|
@ -71,7 +71,11 @@ SpecialPowersObserverAPI.prototype = {
|
|||
|
||||
_observe: function(aSubject, aTopic, aData) {
|
||||
function addDumpIDToMessage(propertyName) {
|
||||
try {
|
||||
var id = aSubject.getPropertyAsAString(propertyName);
|
||||
} catch(ex) {
|
||||
var id = null;
|
||||
}
|
||||
if (id) {
|
||||
message.dumpIDs.push({id: id, extension: "dmp"});
|
||||
message.dumpIDs.push({id: id, extension: "extra"});
|
||||
|
|
|
@ -430,7 +430,7 @@
|
|||
<parameter name="event"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this._editingColumn || this.view.rowCount == 0)
|
||||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this._isAccelPressed(event) && this.view.selection.single) {
|
||||
|
@ -472,7 +472,7 @@
|
|||
<parameter name="event"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this._editingColumn || this.view.rowCount == 0)
|
||||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this.view.selection.single) {
|
||||
|
@ -509,7 +509,7 @@
|
|||
<parameter name="event"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this._editingColumn || this.view.rowCount == 0)
|
||||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this.pageUpOrDownMovesSelection == this._isAccelPressed(event)) {
|
||||
|
@ -558,7 +558,7 @@
|
|||
<parameter name="event"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this._editingColumn || this.view.rowCount == 0)
|
||||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this.view.rowCount == 1 && !this.view.selection.isSelected(0) &&
|
||||
|
@ -608,7 +608,7 @@
|
|||
<parameter name="event"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this._editingColumn || this.view.rowCount == 0)
|
||||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this.view.selection.isSelected(edge) && this.view.selection.count == 1) {
|
||||
|
@ -634,7 +634,7 @@
|
|||
<parameter name="event"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this._editingColumn || this.view.rowCount == 0)
|
||||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
|
||||
|
@ -863,30 +863,102 @@
|
|||
}
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_UP" preventdefault="true"
|
||||
modifiers="accel any" action="_moveByOffset(-1, 0, event);"/>
|
||||
<handler event="keydown" keycode="VK_DOWN" preventdefault="true"
|
||||
modifiers="accel any" action="_moveByOffset(1, this.view.rowCount - 1, event);"/>
|
||||
<handler event="keydown" keycode="VK_UP" preventdefault="true"
|
||||
modifiers="accel any, shift" action="_moveByOffsetShift(-1, 0, event);"/>
|
||||
<handler event="keydown" keycode="VK_DOWN" preventdefault="true"
|
||||
modifiers="accel any, shift" action="_moveByOffsetShift(1, this.view.rowCount - 1, event);"/>
|
||||
<handler event="keydown" keycode="VK_PAGE_UP" preventdefault="true"
|
||||
modifiers="accel any" action="_moveByPage(-1, 0, event);"/>
|
||||
<handler event="keydown" keycode="VK_PAGE_DOWN" preventdefault="true"
|
||||
modifiers="accel any" action="_moveByPage(1, this.view.rowCount - 1, event);"/>
|
||||
<handler event="keydown" keycode="VK_PAGE_UP" preventdefault="true"
|
||||
modifiers="accel any, shift" action="_moveByPageShift(-1, 0, event);"/>
|
||||
<handler event="keydown" keycode="VK_PAGE_DOWN" preventdefault="true"
|
||||
modifiers="accel any, shift" action="_moveByPageShift(1, this.view.rowCount - 1, event);"/>
|
||||
<handler event="keydown" keycode="VK_HOME" preventdefault="true"
|
||||
modifiers="accel any" action="_moveToEdge(0, event);"/>
|
||||
<handler event="keydown" keycode="VK_END" preventdefault="true"
|
||||
modifiers="accel any" action="_moveToEdge(this.view.rowCount - 1, event);"/>
|
||||
<handler event="keydown" keycode="VK_HOME" preventdefault="true"
|
||||
modifiers="accel any, shift" action="_moveToEdgeShift(0, event);"/>
|
||||
<handler event="keydown" keycode="VK_END" preventdefault="true"
|
||||
modifiers="accel any, shift" action="_moveToEdgeShift(this.view.rowCount - 1, event);"/>
|
||||
<handler event="keydown" keycode="VK_UP" modifiers="accel any">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByOffset(-1, 0, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_DOWN" modifiers="accel any">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByOffset(1, this.view.rowCount - 1, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_UP" modifiers="accel any, shift">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByOffsetShift(-1, 0, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_DOWN" modifiers="accel any, shift">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByOffsetShift(1, this.view.rowCount - 1, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_PAGE_UP" modifiers="accel any">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByPage(-1, 0, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_PAGE_DOWN" modifiers="accel any">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByPage(1, this.view.rowCount - 1, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_PAGE_UP" modifiers="accel any, shift">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByPageShift(-1, 0, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_PAGE_DOWN" modifiers="accel any, shift">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveByPageShift(1, this.view.rowCount - 1, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_HOME" modifiers="accel any">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveToEdge(0, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_END" modifiers="accel any">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveToEdge(this.view.rowCount - 1, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_HOME" modifiers="accel any, shift">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveToEdgeShift(0, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keydown" keycode="VK_END" modifiers="accel any, shift">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
return;
|
||||
_moveToEdgeShift(this.view.rowCount - 1, event);
|
||||
event.preventDefault();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="keypress">
|
||||
<![CDATA[
|
||||
if (this._editingColumn)
|
||||
|
|
|
@ -113,13 +113,13 @@ if (LINUX) {
|
|||
}
|
||||
}
|
||||
|
||||
let removed = false;
|
||||
do {
|
||||
try {
|
||||
if (profileDir) {
|
||||
yield OS.File.removeDir(profileDir.parent.parent.path, { ignoreAbsent: true });
|
||||
}
|
||||
|
||||
let removed = false;
|
||||
do {
|
||||
try {
|
||||
yield OS.File.removeDir(installPath, { ignoreAbsent: true });
|
||||
|
||||
yield OS.File.remove(desktopShortcut, { ignoreAbsent: true });
|
||||
|
|
|
@ -113,13 +113,13 @@ if (LINUX) {
|
|||
}
|
||||
}
|
||||
|
||||
let removed = false;
|
||||
do {
|
||||
try {
|
||||
if (profileDir) {
|
||||
yield OS.File.removeDir(profileDir.parent.parent.path, { ignoreAbsent: true });
|
||||
}
|
||||
|
||||
let removed = false;
|
||||
do {
|
||||
try {
|
||||
yield OS.File.removeDir(installPath, { ignoreAbsent: true });
|
||||
|
||||
yield OS.File.remove(desktopShortcut, { ignoreAbsent: true });
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче