This commit is contained in:
Ryan VanderMeulen 2014-05-16 15:31:28 -04:00
Родитель 82b82201a8 641a14a3df
Коммит 2c26dad1a0
104 изменённых файлов: 2504 добавлений и 1213 удалений

Просмотреть файл

@ -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(&notPointerEqual);
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, &notAtom);
branchTest32(Assembler::Zero, temp, atomBit, &notAtom);
branchTest32(Assembler::Zero, Address(left, JSString::offsetOfFlags()), atomBit, &notAtom);
branchTest32(Assembler::Zero, Address(right, JSString::offsetOfFlags()), atomBit, &notAtom);
cmpPtrSet(JSOpToCondition(MCompare::Compare_String, op), left, right, result);
jump(&done);
bind(&notAtom);
// 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 });

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше