зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
0f24f14f35
|
@ -974,3 +974,6 @@ pref("dom.wakelock.enabled", true);
|
|||
pref("services.sync.fxaccounts.enabled", true);
|
||||
pref("identity.fxaccounts.enabled", true);
|
||||
#endif
|
||||
|
||||
// Enable mapped array buffer
|
||||
pref("dom.mapped_arraybuffer.enabled", true);
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<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="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<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"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
|
||||
|
@ -110,7 +110,7 @@
|
|||
<project name="platform/ndk" path="ndk" revision="cb5519af32ae7b4a9c334913a612462ecd04c5d0"/>
|
||||
<project name="platform/prebuilts/misc" path="prebuilts/misc" revision="99c9a644e84a1b0e0a5d240406753b6bc4caca54"/>
|
||||
<project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="6aa61f8557a22039a30b42b7f283996381fd625d"/>
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="21a68e06a104be3f98d776a5bb34ec98643c5bc9"/>
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="e4ab05cf394cc990d66c81ae4a04cce3be77ab26"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<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="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -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="b1d23890531de539e47e594e07e06b2dca54a85b"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="bbbeb3aeb0ae9f209eb8abe48a71f37e568b63af"/>
|
||||
<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": "7a8ed4b21466d68e078529a3737bc5677ba3692f",
|
||||
"revision": "4393a8d719e7917f543cd8d906632c9b2164198e",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<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"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<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"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<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"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<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="7973e06dc278f67b4109ac3c33020ed086f0d042"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="799b0f8bb71bc1b944f90c117ab5d6be4837ba1f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="03c9b9a68af64b72779bede98759464d3beee863"/>
|
||||
<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"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fd4be2909b788b563435fe0addb1bd98dfa75457"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
|
|
@ -336,11 +336,14 @@ Experiments.Policy.prototype = {
|
|||
};
|
||||
|
||||
function AlreadyShutdownError(message="already shut down") {
|
||||
Error.call(this, message);
|
||||
let error = new Error();
|
||||
this.name = "AlreadyShutdownError";
|
||||
this.message = message;
|
||||
this.stack = error.stack;
|
||||
}
|
||||
|
||||
AlreadyShutdownError.prototype = new Error();
|
||||
AlreadyShutdownError.prototype = Object.create(Error.prototype);
|
||||
AlreadyShutdownError.prototype.constructor = AlreadyShutdownError;
|
||||
|
||||
/**
|
||||
|
@ -407,22 +410,19 @@ Experiments.Experiments.prototype = {
|
|||
|
||||
this._registerWithAddonManager();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
this._loadTask = this._loadFromCache();
|
||||
this._loadTask.then(
|
||||
|
||||
return this._loadTask.then(
|
||||
() => {
|
||||
this._log.trace("_loadTask finished ok");
|
||||
this._loadTask = null;
|
||||
this._run().then(deferred.resolve, deferred.reject);
|
||||
return this._run();
|
||||
},
|
||||
(e) => {
|
||||
this._log.error("_loadFromCache caught error: " + e);
|
||||
deferred.reject(e);
|
||||
throw e;
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -670,18 +670,22 @@ Experiments.Experiments.prototype = {
|
|||
this._log.trace("_run");
|
||||
this._checkForShutdown();
|
||||
if (!this._mainTask) {
|
||||
this._mainTask = Task.spawn(this._main.bind(this));
|
||||
this._mainTask.then(
|
||||
() => {
|
||||
this._log.trace("_main finished, scheduling next run");
|
||||
this._mainTask = null;
|
||||
this._scheduleNextRun();
|
||||
},
|
||||
(e) => {
|
||||
this._mainTask = Task.spawn(function*() {
|
||||
try {
|
||||
yield this._main();
|
||||
} catch (e) {
|
||||
this._log.error("_main caught error: " + e);
|
||||
return;
|
||||
} finally {
|
||||
this._mainTask = null;
|
||||
}
|
||||
);
|
||||
this._log.trace("_main finished, scheduling next run");
|
||||
try {
|
||||
yield this._scheduleNextRun();
|
||||
} catch (ex if ex instanceof AlreadyShutdownError) {
|
||||
// We error out of tasks after shutdown via that exception.
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
return this._mainTask;
|
||||
},
|
||||
|
|
|
@ -1564,7 +1564,7 @@ add_task(function* test_foreignUninstallAndRestart() {
|
|||
Assert.ok(!experimentList[0].active, "Experiment 1 should not be active anymore.");
|
||||
|
||||
// Fake restart behaviour.
|
||||
experiments.uninit();
|
||||
yield experiments.uninit();
|
||||
restartManager();
|
||||
experiments = new Experiments.Experiments(gPolicy);
|
||||
yield experiments.updateManifest();
|
||||
|
|
|
@ -1464,9 +1464,6 @@ private void CancelNotification()
|
|||
Log.e("SUTAgentAndroid", "Cannot access world writeable test root");
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
SUTAgentAndroid.sTestRoot = sErrorPrefix + " unable to determine test root";
|
||||
}
|
||||
}
|
||||
|
||||
public String GetTestRoot()
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
#include "nsXMLHttpRequest.h"
|
||||
|
||||
#ifndef XP_WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/dom/XMLHttpRequestUploadBinding.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
|
@ -15,6 +18,7 @@
|
|||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMProgressEvent.h"
|
||||
#include "nsIJARChannel.h"
|
||||
#include "nsIJARURI.h"
|
||||
#include "nsLayoutCID.h"
|
||||
#include "nsReadableUtils.h"
|
||||
|
||||
|
@ -69,8 +73,10 @@
|
|||
#include "nsStreamListenerWrapper.h"
|
||||
#include "xpcjsid.h"
|
||||
#include "nsITimedChannel.h"
|
||||
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsZipArchive.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "private/pprio.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -296,6 +302,7 @@ nsXMLHttpRequest::nsXMLHttpRequest()
|
|||
mInLoadProgressEvent(false),
|
||||
mResultJSON(JSVAL_VOID),
|
||||
mResultArrayBuffer(nullptr),
|
||||
mIsMappedArrayBuffer(false),
|
||||
mXPCOMifier(nullptr)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
|
@ -1748,7 +1755,8 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
|
|||
if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
|
||||
xmlHttpRequest->mResponseBlob = nullptr;
|
||||
}
|
||||
} else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
|
||||
} else if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
|
||||
!xmlHttpRequest->mIsMappedArrayBuffer) ||
|
||||
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
|
||||
// get the initial capacity to something reasonable to avoid a bunch of reallocs right
|
||||
// at the start
|
||||
|
@ -1955,12 +1963,46 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
|
|||
|
||||
// Set up arraybuffer
|
||||
if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER && NS_SUCCEEDED(status)) {
|
||||
int64_t contentLength;
|
||||
rv = channel->GetContentLength(&contentLength);
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
contentLength > 0 &&
|
||||
contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
|
||||
mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
|
||||
if (mIsMappedArrayBuffer) {
|
||||
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
|
||||
if (jarChannel) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = channel->GetURI(getter_AddRefs(uri));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsAutoCString file;
|
||||
nsAutoCString scheme;
|
||||
uri->GetScheme(scheme);
|
||||
if (scheme.LowerCaseEqualsLiteral("app")) {
|
||||
uri->GetPath(file);
|
||||
// The actual file inside zip package has no leading slash.
|
||||
file.Trim("/", true, false, false);
|
||||
} else if (scheme.LowerCaseEqualsLiteral("jar")) {
|
||||
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
|
||||
if (jarURI) {
|
||||
jarURI->GetJAREntry(file);
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIFile> jarFile;
|
||||
jarChannel->GetJarFile(getter_AddRefs(jarFile));
|
||||
rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mIsMappedArrayBuffer = false;
|
||||
} else {
|
||||
channel->SetContentType(NS_LITERAL_CSTRING("application/mem-mapped"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If memory mapping failed, mIsMappedArrayBuffer would be set to false,
|
||||
// and we want it fallback to the malloc way.
|
||||
if (!mIsMappedArrayBuffer) {
|
||||
int64_t contentLength;
|
||||
rv = channel->GetContentLength(&contentLength);
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
contentLength > 0 &&
|
||||
contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
|
||||
mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2145,7 +2187,8 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
|
|||
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
|
||||
NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
|
||||
} else if (NS_SUCCEEDED(status) &&
|
||||
(mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
|
||||
((mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
|
||||
!mIsMappedArrayBuffer) ||
|
||||
mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
|
||||
// set the capacity down to the actual length, to realloc back
|
||||
// down to the actual size
|
||||
|
@ -2880,6 +2923,21 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
mIsMappedArrayBuffer = false;
|
||||
if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
|
||||
Preferences::GetBool("dom.mapped_arraybuffer.enabled", false)) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsAutoCString scheme;
|
||||
|
||||
rv = mChannel->GetURI(getter_AddRefs(uri));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
uri->GetScheme(scheme);
|
||||
if (scheme.LowerCaseEqualsLiteral("app") ||
|
||||
scheme.LowerCaseEqualsLiteral("jar")) {
|
||||
mIsMappedArrayBuffer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Start reading from the channel
|
||||
rv = mChannel->AsyncOpen(listener, nullptr);
|
||||
}
|
||||
|
@ -3844,7 +3902,8 @@ namespace mozilla {
|
|||
ArrayBufferBuilder::ArrayBufferBuilder()
|
||||
: mDataPtr(nullptr),
|
||||
mCapacity(0),
|
||||
mLength(0)
|
||||
mLength(0),
|
||||
mMapPtr(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -3859,6 +3918,12 @@ ArrayBufferBuilder::reset()
|
|||
if (mDataPtr) {
|
||||
JS_free(nullptr, mDataPtr);
|
||||
}
|
||||
|
||||
if (mMapPtr) {
|
||||
JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
|
||||
mMapPtr = nullptr;
|
||||
}
|
||||
|
||||
mDataPtr = nullptr;
|
||||
mCapacity = mLength = 0;
|
||||
}
|
||||
|
@ -3866,6 +3931,8 @@ ArrayBufferBuilder::reset()
|
|||
bool
|
||||
ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
|
||||
{
|
||||
MOZ_ASSERT(!mMapPtr);
|
||||
|
||||
uint8_t *newdata = (uint8_t *) JS_ReallocateArrayBufferContents(nullptr, aNewCap, mDataPtr, mCapacity);
|
||||
if (!newdata) {
|
||||
return false;
|
||||
|
@ -3884,6 +3951,8 @@ bool
|
|||
ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
|
||||
uint32_t aMaxGrowth)
|
||||
{
|
||||
MOZ_ASSERT(!mMapPtr);
|
||||
|
||||
if (mLength + aDataLen > mCapacity) {
|
||||
uint32_t newcap;
|
||||
// Double while under aMaxGrowth or if not specified.
|
||||
|
@ -3921,6 +3990,18 @@ ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
|
|||
JSObject*
|
||||
ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
|
||||
{
|
||||
if (mMapPtr) {
|
||||
JSObject* obj = JS_NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
|
||||
if (!obj) {
|
||||
JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
|
||||
}
|
||||
mMapPtr = nullptr;
|
||||
|
||||
// The memory-mapped contents will be released when obj been finalized(GCed
|
||||
// or neutered).
|
||||
return obj;
|
||||
}
|
||||
|
||||
// we need to check for mLength == 0, because nothing may have been
|
||||
// added
|
||||
if (mCapacity > mLength || mLength == 0) {
|
||||
|
@ -3939,6 +4020,50 @@ ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
|
|||
return obj;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
|
||||
nsIFile* aJarFile)
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
// TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
|
||||
MOZ_CRASH("Not implemented");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#else
|
||||
nsresult rv;
|
||||
|
||||
// Open Jar file to get related attributes of target file.
|
||||
nsRefPtr<nsZipArchive> zip = new nsZipArchive();
|
||||
rv = zip->OpenArchive(aJarFile);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsZipItem* zipItem = zip->GetItem(aFile.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If file was added to the package as stored(uncompressed), map to the
|
||||
// offset of file in zip package.
|
||||
if (!zipItem->Compression()) {
|
||||
uint32_t offset = zip->GetDataOffset(zipItem);
|
||||
uint32_t size = zipItem->RealSize();
|
||||
mozilla::AutoFDClose pr_fd;
|
||||
mozilla::ScopedClose fd;
|
||||
rv = aJarFile->OpenNSPRFileDesc(PR_RDONLY, 0, &pr_fd.rwget());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
fd.rwget() = PR_FileDesc2NativeHandle(pr_fd);
|
||||
mMapPtr = JS_CreateMappedArrayBufferContents(fd, offset, size);
|
||||
if (mMapPtr) {
|
||||
mLength = size;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
|
||||
uint32_t aLength1,
|
||||
|
|
|
@ -65,6 +65,7 @@ class ArrayBufferBuilder
|
|||
uint8_t* mDataPtr;
|
||||
uint32_t mCapacity;
|
||||
uint32_t mLength;
|
||||
void* mMapPtr;
|
||||
public:
|
||||
ArrayBufferBuilder();
|
||||
~ArrayBufferBuilder();
|
||||
|
@ -89,6 +90,14 @@ public:
|
|||
|
||||
JSObject* getArrayBuffer(JSContext* aCx);
|
||||
|
||||
// Memory mapping to starting position of file(aFile) in the zip
|
||||
// package(aJarFile).
|
||||
//
|
||||
// The file in the zip package has to be uncompressed and the starting
|
||||
// position of the file must be aligned according to array buffer settings
|
||||
// in JS engine.
|
||||
nsresult mapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile);
|
||||
|
||||
protected:
|
||||
static bool areOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
|
||||
const uint8_t* aStart2, uint32_t aLength2);
|
||||
|
@ -727,6 +736,7 @@ protected:
|
|||
|
||||
mozilla::ArrayBufferBuilder mArrayBufferBuilder;
|
||||
JS::Heap<JSObject*> mResultArrayBuffer;
|
||||
bool mIsMappedArrayBuffer;
|
||||
|
||||
void ResetResponse();
|
||||
|
||||
|
|
Двоичный файл не отображается.
|
@ -0,0 +1,98 @@
|
|||
var gData1 = "TEST_DATA_1:ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
var gData2 = "TEST_DATA_2:1234567890";
|
||||
var gPaddingChar = '.';
|
||||
var gPaddingSize = 10000;
|
||||
var gPadding = "";
|
||||
|
||||
for (var i = 0; i < gPaddingSize; i++) {
|
||||
gPadding += gPaddingChar;
|
||||
}
|
||||
|
||||
function ok(a, msg) {
|
||||
postMessage({type: 'status', status: !!a, msg: msg });
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
postMessage({type: 'status', status: a === b, msg: msg });
|
||||
}
|
||||
|
||||
function checkData(response, data_head, cb) {
|
||||
ok(response, "Data is non-null");
|
||||
var str = String.fromCharCode.apply(null, Uint8Array(response));
|
||||
ok(str.length == data_head.length + gPaddingSize, "Data size is correct");
|
||||
ok(str.slice(0, data_head.length) == data_head, "Data head is correct");
|
||||
ok(str.slice(data_head.length) == gPadding, "Data padding is correct");
|
||||
cb();
|
||||
}
|
||||
|
||||
self.onmessage = function onmessage(event) {
|
||||
|
||||
function test_mapped_sync() {
|
||||
var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
|
||||
xhr.open('GET', 'jar:http://example.org/tests/content/base/test/file_bug945152.jar!/data_1.txt', false);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.send();
|
||||
if (xhr.status) {
|
||||
ok(xhr.status == 200, "Status is 200");
|
||||
var ct = xhr.getResponseHeader("Content-Type");
|
||||
ok(ct.indexOf("mem-mapped") != -1, "Data is memory-mapped");
|
||||
checkData(xhr.response, gData1, runTests);
|
||||
}
|
||||
}
|
||||
|
||||
function test_mapped_async() {
|
||||
var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
|
||||
xhr.open('GET', 'jar:http://example.org/tests/content/base/test/file_bug945152.jar!/data_1.txt');
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState !== xhr.DONE) {
|
||||
return;
|
||||
}
|
||||
if (xhr.status) {
|
||||
ok(xhr.status == 200, "Status is 200");
|
||||
var ct = xhr.getResponseHeader("Content-Type");
|
||||
ok(ct.indexOf("mem-mapped") != -1, "Data is memory-mapped");
|
||||
checkData(xhr.response, gData1, runTests);
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
// Make sure array buffer retrieved from compressed file in package is
|
||||
// handled by memory allocation instead of memory mapping.
|
||||
function test_non_mapped() {
|
||||
var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
|
||||
xhr.open('GET', 'jar:http://example.org/tests/content/base/test/file_bug945152.jar!/data_2.txt');
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState !== xhr.DONE) {
|
||||
return;
|
||||
}
|
||||
if (xhr.status) {
|
||||
ok(xhr.status == 200, "Status is 200");
|
||||
var ct = xhr.getResponseHeader("Content-Type");
|
||||
ok(ct.indexOf("mem-mapped") == -1, "Data is not memory-mapped");
|
||||
checkData(xhr.response, gData2, runTests);
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_mapped_sync,
|
||||
test_mapped_async,
|
||||
test_non_mapped
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
postMessage({type: 'finish' });
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
runTests();
|
||||
};
|
|
@ -135,6 +135,8 @@ support-files =
|
|||
file_bug902350.html
|
||||
file_bug902350_frame.html
|
||||
file_bug907892.html
|
||||
file_bug945152.jar
|
||||
file_bug945152_worker.js
|
||||
file_general_document.html
|
||||
file_html_in_xhr.html
|
||||
file_html_in_xhr.sjs
|
||||
|
@ -544,6 +546,8 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #needs plugin suppor
|
|||
[test_bug907892.html]
|
||||
[test_bug922681.html]
|
||||
[test_bug927196.html]
|
||||
[test_bug945152.html]
|
||||
run-if = os == 'linux'
|
||||
[test_caretPositionFromPoint.html]
|
||||
[test_classList.html]
|
||||
# This test fails on the Mac for some reason
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=945152
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 945152</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=945152">Mozilla Bug 945152</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function runTest() {
|
||||
var worker = new Worker("file_bug945152_worker.js");
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
if (event.data.type == 'finish') {
|
||||
SimpleTest.finish();
|
||||
} else if (event.data.type == 'status') {
|
||||
ok(event.data.status, event.data.msg);
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = function(event) {
|
||||
is(event.target, worker);
|
||||
ok(false, "Worker had an error: " + event.data);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
worker.postMessage(true);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.mapped_arraybuffer.enabled", true]]}, function() {
|
||||
SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runTest);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -38,6 +38,16 @@ AudioData::EnsureAudioBuffer()
|
|||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
|
||||
if (mAudioBuffer) {
|
||||
size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
|
||||
{
|
||||
|
|
|
@ -80,13 +80,7 @@ public:
|
|||
MOZ_COUNT_DTOR(AudioData);
|
||||
}
|
||||
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
|
||||
size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
|
||||
if (mAudioBuffer) {
|
||||
size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
// If mAudioBuffer is null, creates it from mAudioData.
|
||||
void EnsureAudioBuffer();
|
||||
|
|
|
@ -577,7 +577,7 @@ WaveReader::LoadListChunk(uint32_t aChunkSize,
|
|||
uint32_t length = ReadUint32LE(&p);
|
||||
|
||||
// Subchunk shall not exceed parent chunk.
|
||||
if (p + length > end) {
|
||||
if (uint32_t(end - p) < length) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,12 +41,13 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioBuffer, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioBuffer, Release)
|
||||
|
||||
AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aLength,
|
||||
float aSampleRate)
|
||||
AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
|
||||
uint32_t aLength, float aSampleRate)
|
||||
: mContext(aContext),
|
||||
mLength(aLength),
|
||||
mSampleRate(aSampleRate)
|
||||
{
|
||||
mJSChannels.SetCapacity(aNumberOfChannels);
|
||||
SetIsDOMBinding();
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
@ -63,22 +64,36 @@ AudioBuffer::ClearJSChannels()
|
|||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
bool
|
||||
AudioBuffer::InitializeBuffers(uint32_t aNumberOfChannels, JSContext* aJSContext)
|
||||
/* static */ already_AddRefed<AudioBuffer>
|
||||
AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
|
||||
uint32_t aLength, float aSampleRate,
|
||||
JSContext* aJSContext, ErrorResult& aRv)
|
||||
{
|
||||
if (!mJSChannels.SetCapacity(aNumberOfChannels)) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
|
||||
JS::Rooted<JSObject*> array(aJSContext,
|
||||
JS_NewFloat32Array(aJSContext, mLength));
|
||||
if (!array) {
|
||||
return false;
|
||||
}
|
||||
mJSChannels.AppendElement(array.get());
|
||||
// Note that a buffer with zero channels is permitted here for the sake of
|
||||
// AudioProcessingEvent, where channel counts must match parameters passed
|
||||
// to createScriptProcessor(), one of which may be zero.
|
||||
if (aSampleRate < WebAudioUtils::MinSampleRate ||
|
||||
aSampleRate > WebAudioUtils::MaxSampleRate ||
|
||||
aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
|
||||
!aLength || aLength > INT32_MAX) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
nsRefPtr<AudioBuffer> buffer =
|
||||
new AudioBuffer(aContext, aNumberOfChannels, aLength, aSampleRate);
|
||||
|
||||
for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
|
||||
JS::Rooted<JSObject*> array(aJSContext,
|
||||
JS_NewFloat32Array(aJSContext, aLength));
|
||||
if (!array) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
buffer->mJSChannels.AppendElement(array.get());
|
||||
}
|
||||
|
||||
return buffer.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
|
|
@ -33,17 +33,13 @@ class AudioContext;
|
|||
class AudioBuffer MOZ_FINAL : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
AudioBuffer(AudioContext* aContext, uint32_t aLength,
|
||||
float aSampleRate);
|
||||
~AudioBuffer();
|
||||
static already_AddRefed<AudioBuffer>
|
||||
Create(AudioContext* aContext, uint32_t aNumberOfChannels,
|
||||
uint32_t aLength, float aSampleRate,
|
||||
JSContext* aJSContext, ErrorResult& aRv);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
// This function needs to be called in order to allocate
|
||||
// all of the channels. It is fallible!
|
||||
bool InitializeBuffers(uint32_t aNumberOfChannels,
|
||||
JSContext* aJSContext);
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioBuffer)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioBuffer)
|
||||
|
||||
|
@ -100,12 +96,16 @@ public:
|
|||
void SetRawChannelContents(uint32_t aChannel, float* aContents);
|
||||
|
||||
protected:
|
||||
AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
|
||||
uint32_t aLength, float aSampleRate);
|
||||
~AudioBuffer();
|
||||
|
||||
bool RestoreJSChannelData(JSContext* aJSContext);
|
||||
void ClearJSChannels();
|
||||
|
||||
nsRefPtr<AudioContext> mContext;
|
||||
// Float32Arrays
|
||||
AutoFallibleTArray<JS::Heap<JSObject*>, 2> mJSChannels;
|
||||
nsAutoTArray<JS::Heap<JSObject*>, 2> mJSChannels;
|
||||
|
||||
// mSharedChannels aggregates the data from mJSChannels. This is non-null
|
||||
// if and only if the mJSChannels are neutered.
|
||||
|
|
|
@ -172,8 +172,8 @@ AudioContext::Constructor(const GlobalObject& aGlobal,
|
|||
if (aNumberOfChannels == 0 ||
|
||||
aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
|
||||
aLength == 0 ||
|
||||
aSampleRate <= 1.0f ||
|
||||
aSampleRate >= TRACK_RATE_MAX) {
|
||||
aSampleRate < WebAudioUtils::MinSampleRate ||
|
||||
aSampleRate > WebAudioUtils::MaxSampleRate) {
|
||||
// The DOM binding protects us against infinity and NaN
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
|
@ -204,24 +204,13 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
|
|||
uint32_t aLength, float aSampleRate,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aSampleRate < 8000 || aSampleRate > 192000 || !aLength || !aNumberOfChannels) {
|
||||
if (!aNumberOfChannels) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aLength > INT32_MAX) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<AudioBuffer> buffer =
|
||||
new AudioBuffer(this, int32_t(aLength), aSampleRate);
|
||||
if (!buffer->InitializeBuffers(aNumberOfChannels, aJSContext)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return buffer.forget();
|
||||
return AudioBuffer::Create(this, aNumberOfChannels, aLength,
|
||||
aSampleRate, aJSContext, aRv);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -137,10 +137,11 @@ public:
|
|||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
// Create the input buffer
|
||||
nsRefPtr<AudioBuffer> renderedBuffer = new AudioBuffer(context,
|
||||
mLength,
|
||||
mSampleRate);
|
||||
if (!renderedBuffer->InitializeBuffers(mInputChannels.Length(), cx)) {
|
||||
ErrorResult rv;
|
||||
nsRefPtr<AudioBuffer> renderedBuffer =
|
||||
AudioBuffer::Create(context, mInputChannels.Length(),
|
||||
mLength, mSampleRate, cx, rv);
|
||||
if (rv.Failed()) {
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
|
||||
|
|
|
@ -37,23 +37,27 @@ AudioProcessingEvent::WrapObject(JSContext* aCx)
|
|||
return AudioProcessingEventBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
void
|
||||
AudioProcessingEvent::LazilyCreateBuffer(nsRefPtr<AudioBuffer>& aBuffer,
|
||||
uint32_t aNumberOfChannels)
|
||||
already_AddRefed<AudioBuffer>
|
||||
AudioProcessingEvent::LazilyCreateBuffer(uint32_t aNumberOfChannels,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// We need the global for the context so that we can enter its compartment.
|
||||
JSObject* global = mNode->Context()->GetGlobalJSObject();
|
||||
if (NS_WARN_IF(!global)) {
|
||||
return;
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
JSContext* cx = jsapi.cx();
|
||||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
aBuffer = new AudioBuffer(mNode->Context(), mNode->BufferSize(),
|
||||
mNode->Context()->SampleRate());
|
||||
aBuffer->InitializeBuffers(aNumberOfChannels, cx);
|
||||
nsRefPtr<AudioBuffer> buffer =
|
||||
AudioBuffer::Create(mNode->Context(), aNumberOfChannels,
|
||||
mNode->BufferSize(),
|
||||
mNode->Context()->SampleRate(), cx, aRv);
|
||||
MOZ_ASSERT(buffer || aRv.ErrorCode() == NS_ERROR_OUT_OF_MEMORY);
|
||||
return buffer.forget();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,18 +42,18 @@ public:
|
|||
return mPlaybackTime;
|
||||
}
|
||||
|
||||
AudioBuffer* InputBuffer()
|
||||
AudioBuffer* GetInputBuffer(ErrorResult& aRv)
|
||||
{
|
||||
if (!mInputBuffer) {
|
||||
LazilyCreateBuffer(mInputBuffer, mNumberOfInputChannels);
|
||||
mInputBuffer = LazilyCreateBuffer(mNumberOfInputChannels, aRv);
|
||||
}
|
||||
return mInputBuffer;
|
||||
}
|
||||
|
||||
AudioBuffer* OutputBuffer()
|
||||
AudioBuffer* GetOutputBuffer(ErrorResult& aRv)
|
||||
{
|
||||
if (!mOutputBuffer) {
|
||||
LazilyCreateBuffer(mOutputBuffer, mNode->NumberOfOutputChannels());
|
||||
mOutputBuffer = LazilyCreateBuffer(mNode->NumberOfOutputChannels(), aRv);
|
||||
}
|
||||
return mOutputBuffer;
|
||||
}
|
||||
|
@ -64,8 +64,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
void LazilyCreateBuffer(nsRefPtr<AudioBuffer>& aBuffer,
|
||||
uint32_t aNumberOfChannels);
|
||||
already_AddRefed<AudioBuffer>
|
||||
LazilyCreateBuffer(uint32_t aNumberOfChannels, ErrorResult& rv);
|
||||
|
||||
private:
|
||||
double mPlaybackTime;
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
MOZ_ASSERT(aInput.Length() >= 1, "Should have one or more input ports");
|
||||
|
||||
// Get the number of output channels, and allocate it
|
||||
uint32_t channelCount = 0;
|
||||
size_t channelCount = 0;
|
||||
for (uint16_t i = 0; i < InputCount(); ++i) {
|
||||
channelCount += aInput[i].mChannelData.Length();
|
||||
}
|
||||
|
@ -39,17 +39,22 @@ public:
|
|||
aOutput[0].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
}
|
||||
channelCount = std::min(channelCount, WebAudioUtils::MaxChannelCount);
|
||||
AllocateAudioBlock(channelCount, &aOutput[0]);
|
||||
|
||||
// Append each channel in each input to the output
|
||||
uint32_t channelIndex = 0;
|
||||
for (uint16_t i = 0; i < InputCount(); ++i) {
|
||||
for (uint32_t j = 0; j < aInput[i].mChannelData.Length(); ++j) {
|
||||
size_t channelIndex = 0;
|
||||
for (uint16_t i = 0; true; ++i) {
|
||||
MOZ_ASSERT(i < InputCount());
|
||||
for (size_t j = 0; j < aInput[i].mChannelData.Length(); ++j) {
|
||||
AudioBlockCopyChannelWithScale(
|
||||
static_cast<const float*>(aInput[i].mChannelData[j]),
|
||||
aInput[i].mVolume,
|
||||
static_cast<float*>(const_cast<void*>(aOutput[0].mChannelData[channelIndex])));
|
||||
++channelIndex;
|
||||
if (channelIndex >= channelCount) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -413,8 +413,10 @@ WebAudioDecodeJob::AllocateBuffer()
|
|||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
// Now create the AudioBuffer
|
||||
mOutput = new AudioBuffer(mContext, mWriteIndex, mContext->SampleRate());
|
||||
if (!mOutput->InitializeBuffers(mChannelBuffers.Length(), cx)) {
|
||||
ErrorResult rv;
|
||||
mOutput = AudioBuffer::Create(mContext, mChannelBuffers.Length(),
|
||||
mWriteIndex, mContext->SampleRate(), cx, rv);
|
||||
if (rv.Failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -415,10 +415,12 @@ private:
|
|||
// Create the input buffer
|
||||
nsRefPtr<AudioBuffer> inputBuffer;
|
||||
if (!mNullInput) {
|
||||
inputBuffer = new AudioBuffer(node->Context(),
|
||||
node->BufferSize(),
|
||||
node->Context()->SampleRate());
|
||||
if (!inputBuffer->InitializeBuffers(mInputChannels.Length(), cx)) {
|
||||
ErrorResult rv;
|
||||
inputBuffer =
|
||||
AudioBuffer::Create(node->Context(), mInputChannels.Length(),
|
||||
node->BufferSize(),
|
||||
node->Context()->SampleRate(), cx, rv);
|
||||
if (rv.Failed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Put the channel data inside it
|
||||
|
@ -438,10 +440,18 @@ private:
|
|||
mPlaybackTime);
|
||||
node->DispatchTrustedEvent(event);
|
||||
|
||||
// Steal the output buffers
|
||||
// Steal the output buffers if they have been set.
|
||||
// Don't create a buffer if it hasn't been used to return output;
|
||||
// FinishProducingOutputBuffer() will optimize output = null.
|
||||
// GetThreadSharedChannelsForRate() may also return null after OOM.
|
||||
nsRefPtr<ThreadSharedFloatArrayBufferList> output;
|
||||
if (event->HasOutputBuffer()) {
|
||||
output = event->OutputBuffer()->GetThreadSharedChannelsForRate(cx);
|
||||
ErrorResult rv;
|
||||
AudioBuffer* buffer = event->GetOutputBuffer(rv);
|
||||
// HasOutputBuffer() returning true means that GetOutputBuffer()
|
||||
// will not fail.
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
output = buffer->GetThreadSharedChannelsForRate(cx);
|
||||
}
|
||||
|
||||
// Append it to our output buffer queue
|
||||
|
|
|
@ -14,10 +14,6 @@ namespace mozilla {
|
|||
|
||||
namespace dom {
|
||||
|
||||
// 32 is the minimum required by the spec and matches what is used by blink.
|
||||
// The limit protects against large memory allocations.
|
||||
const size_t WebAudioUtils::MaxChannelCount = 32;
|
||||
|
||||
struct ConvertTimeToTickHelper
|
||||
{
|
||||
AudioNodeStream* mSourceStream;
|
||||
|
|
|
@ -24,15 +24,22 @@ namespace dom {
|
|||
|
||||
class AudioParamTimeline;
|
||||
|
||||
struct WebAudioUtils {
|
||||
static const size_t MaxChannelCount;
|
||||
namespace WebAudioUtils {
|
||||
// 32 is the minimum required by the spec for createBuffer() and
|
||||
// createScriptProcessor() and matches what is used by Blink. The limit
|
||||
// protects against large memory allocations.
|
||||
const size_t MaxChannelCount = 32;
|
||||
// AudioContext::CreateBuffer() "must support sample-rates in at least the
|
||||
// range 22050 to 96000."
|
||||
const uint32_t MinSampleRate = 8000;
|
||||
const uint32_t MaxSampleRate = 192000;
|
||||
|
||||
static bool FuzzyEqual(float v1, float v2)
|
||||
inline bool FuzzyEqual(float v1, float v2)
|
||||
{
|
||||
using namespace std;
|
||||
return fabsf(v1 - v2) < 1e-7f;
|
||||
}
|
||||
static bool FuzzyEqual(double v1, double v2)
|
||||
inline bool FuzzyEqual(double v1, double v2)
|
||||
{
|
||||
using namespace std;
|
||||
return fabs(v1 - v2) < 1e-7;
|
||||
|
@ -42,7 +49,7 @@ struct WebAudioUtils {
|
|||
* Computes an exponential smoothing rate for a time based variable
|
||||
* over aDuration seconds.
|
||||
*/
|
||||
static double ComputeSmoothingRate(double aDuration, double aSampleRate)
|
||||
inline double ComputeSmoothingRate(double aDuration, double aSampleRate)
|
||||
{
|
||||
return 1.0 - std::exp(-1.0 / (aDuration * aSampleRate));
|
||||
}
|
||||
|
@ -56,15 +63,15 @@ struct WebAudioUtils {
|
|||
* received. This means that such engines need to be aware of their source
|
||||
* and destination streams as well.
|
||||
*/
|
||||
static void ConvertAudioParamToTicks(AudioParamTimeline& aParam,
|
||||
AudioNodeStream* aSource,
|
||||
AudioNodeStream* aDest);
|
||||
void ConvertAudioParamToTicks(AudioParamTimeline& aParam,
|
||||
AudioNodeStream* aSource,
|
||||
AudioNodeStream* aDest);
|
||||
|
||||
/**
|
||||
* Converts a linear value to decibels. Returns aMinDecibels if the linear
|
||||
* value is 0.
|
||||
*/
|
||||
static float ConvertLinearToDecibels(float aLinearValue, float aMinDecibels)
|
||||
inline float ConvertLinearToDecibels(float aLinearValue, float aMinDecibels)
|
||||
{
|
||||
return aLinearValue ? 20.0f * std::log10(aLinearValue) : aMinDecibels;
|
||||
}
|
||||
|
@ -72,7 +79,7 @@ struct WebAudioUtils {
|
|||
/**
|
||||
* Converts a decibel value to a linear value.
|
||||
*/
|
||||
static float ConvertDecibelsToLinear(float aDecibels)
|
||||
inline float ConvertDecibelsToLinear(float aDecibels)
|
||||
{
|
||||
return std::pow(10.0f, 0.05f * aDecibels);
|
||||
}
|
||||
|
@ -80,24 +87,24 @@ struct WebAudioUtils {
|
|||
/**
|
||||
* Converts a decibel to a linear value.
|
||||
*/
|
||||
static float ConvertDecibelToLinear(float aDecibel)
|
||||
inline float ConvertDecibelToLinear(float aDecibel)
|
||||
{
|
||||
return std::pow(10.0f, 0.05f * aDecibel);
|
||||
}
|
||||
|
||||
static void FixNaN(double& aDouble)
|
||||
inline void FixNaN(double& aDouble)
|
||||
{
|
||||
if (IsNaN(aDouble) || IsInfinite(aDouble)) {
|
||||
aDouble = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static double DiscreteTimeConstantForSampleRate(double timeConstant, double sampleRate)
|
||||
inline double DiscreteTimeConstantForSampleRate(double timeConstant, double sampleRate)
|
||||
{
|
||||
return 1.0 - std::exp(-1.0 / (sampleRate * timeConstant));
|
||||
}
|
||||
|
||||
static bool IsTimeValid(double aTime)
|
||||
inline bool IsTimeValid(double aTime)
|
||||
{
|
||||
return aTime >= 0 && aTime <= (MEDIA_TIME_MAX >> MEDIA_TIME_FRAC_BITS);
|
||||
}
|
||||
|
@ -165,7 +172,7 @@ struct WebAudioUtils {
|
|||
* it sees a NaN.
|
||||
*/
|
||||
template <typename IntType, typename FloatType>
|
||||
static IntType TruncateFloatToInt(FloatType f)
|
||||
IntType TruncateFloatToInt(FloatType f)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
|
@ -196,26 +203,26 @@ struct WebAudioUtils {
|
|||
return IntType(f);
|
||||
}
|
||||
|
||||
static void Shutdown();
|
||||
void Shutdown();
|
||||
|
||||
static int
|
||||
int
|
||||
SpeexResamplerProcess(SpeexResamplerState* aResampler,
|
||||
uint32_t aChannel,
|
||||
const float* aIn, uint32_t* aInLen,
|
||||
float* aOut, uint32_t* aOutLen);
|
||||
|
||||
static int
|
||||
int
|
||||
SpeexResamplerProcess(SpeexResamplerState* aResampler,
|
||||
uint32_t aChannel,
|
||||
const int16_t* aIn, uint32_t* aInLen,
|
||||
float* aOut, uint32_t* aOutLen);
|
||||
|
||||
static int
|
||||
int
|
||||
SpeexResamplerProcess(SpeexResamplerState* aResampler,
|
||||
uint32_t aChannel,
|
||||
const int16_t* aIn, uint32_t* aInLen,
|
||||
int16_t* aOut, uint32_t* aOutLen);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
using mozilla::dom::WebAudioUtils;
|
||||
using namespace mozilla::dom; // for WebAudioUtils
|
||||
using mozilla::IsInfinite;
|
||||
using mozilla::IsNaN;
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@ public:
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
void OnHardwareStateChange(HardwareState aState);
|
||||
void GetRotation();
|
||||
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
|
||||
void OnUserError(UserContext aContext, nsresult aError);
|
||||
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
|
||||
|
@ -212,9 +213,12 @@ private:
|
|||
|
||||
// Engine variables.
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
nsRefPtr<ICameraControl> mCameraControl;
|
||||
mozilla::ReentrantMonitor mCallbackMonitor; // Monitor for camera callback handling
|
||||
// This is only modified on MainThread (AllocImpl and DeallocImpl)
|
||||
nsRefPtr<ICameraControl> mCameraControl;
|
||||
nsRefPtr<nsIDOMFile> mLastCapture;
|
||||
|
||||
// These are protected by mMonitor below
|
||||
int mRotation;
|
||||
int mCameraAngle; // See dom/base/ScreenOrientation.h
|
||||
bool mBackCamera;
|
||||
|
|
|
@ -415,7 +415,7 @@ MediaEngineWebRTCVideoSource::Allocate(const VideoTrackConstraintsN &aConstraint
|
|||
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
|
||||
if (mState == kReleased && mInitDone) {
|
||||
ChooseCapability(aConstraints, aPrefs);
|
||||
NS_DispatchToMainThread(WrapRunnable(this,
|
||||
NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineWebRTCVideoSource>(this),
|
||||
&MediaEngineWebRTCVideoSource::AllocImpl));
|
||||
mCallbackMonitor.Wait();
|
||||
if (mState != kAllocated) {
|
||||
|
@ -459,7 +459,7 @@ MediaEngineWebRTCVideoSource::Deallocate()
|
|||
#ifdef MOZ_B2G_CAMERA
|
||||
// We do not register success callback here
|
||||
|
||||
NS_DispatchToMainThread(WrapRunnable(this,
|
||||
NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineWebRTCVideoSource>(this),
|
||||
&MediaEngineWebRTCVideoSource::DeallocImpl));
|
||||
mCallbackMonitor.Wait();
|
||||
if (mState != kReleased) {
|
||||
|
@ -519,7 +519,7 @@ MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
|
|||
mImageContainer = layers::LayerManager::CreateImageContainer();
|
||||
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
NS_DispatchToMainThread(WrapRunnable(this,
|
||||
NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineWebRTCVideoSource>(this),
|
||||
&MediaEngineWebRTCVideoSource::StartImpl,
|
||||
mCapability));
|
||||
mCallbackMonitor.Wait();
|
||||
|
@ -573,7 +573,7 @@ MediaEngineWebRTCVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
|||
mImage = nullptr;
|
||||
}
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
NS_DispatchToMainThread(WrapRunnable(this,
|
||||
NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineWebRTCVideoSource>(this),
|
||||
&MediaEngineWebRTCVideoSource::StopImpl));
|
||||
#else
|
||||
mViERender->StopRender(mCaptureIndex);
|
||||
|
@ -789,26 +789,37 @@ MediaEngineWebRTCVideoSource::OnHardwareStateChange(HardwareState aState)
|
|||
mCallbackMonitor.Notify();
|
||||
}
|
||||
} else {
|
||||
mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, mCameraAngle);
|
||||
MOZ_ASSERT(mCameraAngle == 0 || mCameraAngle == 90 || mCameraAngle == 180 ||
|
||||
mCameraAngle == 270);
|
||||
hal::ScreenConfiguration aConfig;
|
||||
hal::GetCurrentScreenConfiguration(&aConfig);
|
||||
|
||||
nsCString deviceName;
|
||||
ICameraControl::GetCameraName(mCaptureIndex, deviceName);
|
||||
if (deviceName.EqualsASCII("back")) {
|
||||
mBackCamera = true;
|
||||
}
|
||||
|
||||
mRotation = GetRotateAmount(aConfig.orientation(), mCameraAngle, mBackCamera);
|
||||
LOG(("*** Initial orientation: %d (Camera %d Back %d MountAngle: %d)",
|
||||
mRotation, mCaptureIndex, mBackCamera, mCameraAngle));
|
||||
// Can't read this except on MainThread (ugh)
|
||||
NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineWebRTCVideoSource>(this),
|
||||
&MediaEngineWebRTCVideoSource::GetRotation));
|
||||
mState = kStarted;
|
||||
mCallbackMonitor.Notify();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCVideoSource::GetRotation()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MonitorAutoLock enter(mMonitor);
|
||||
|
||||
mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, mCameraAngle);
|
||||
MOZ_ASSERT(mCameraAngle == 0 || mCameraAngle == 90 || mCameraAngle == 180 ||
|
||||
mCameraAngle == 270);
|
||||
hal::ScreenConfiguration config;
|
||||
hal::GetCurrentScreenConfiguration(&config);
|
||||
|
||||
nsCString deviceName;
|
||||
ICameraControl::GetCameraName(mCaptureIndex, deviceName);
|
||||
if (deviceName.EqualsASCII("back")) {
|
||||
mBackCamera = true;
|
||||
}
|
||||
|
||||
mRotation = GetRotateAmount(config.orientation(), mCameraAngle, mBackCamera);
|
||||
LOG(("*** Initial orientation: %d (Camera %d Back %d MountAngle: %d)",
|
||||
mRotation, mCaptureIndex, mBackCamera, mCameraAngle));
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCVideoSource::OnUserError(UserContext aContext, nsresult aError)
|
||||
{
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
asmjs/*
|
||||
file_bug_945152.html
|
||||
file_bug_945152.sjs
|
||||
|
||||
[test_apps_service.xul]
|
||||
[test_bug_945152.html]
|
||||
run-if = os == 'linux'
|
||||
[test_operator_app_install.js]
|
||||
[test_operator_app_install.xul]
|
||||
# bug 928262
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
<html>
|
||||
<head>
|
||||
<script>
|
||||
|
||||
var gData1 = "TEST_DATA_1:ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
var gData2 = "TEST_DATA_2:1234567890";
|
||||
var gPaddingChar = '.';
|
||||
var gPaddingSize = 10000;
|
||||
var gPadding = "";
|
||||
|
||||
for (var i = 0; i < gPaddingSize; i++) {
|
||||
gPadding += gPaddingChar;
|
||||
}
|
||||
|
||||
function sendMessage(msg) {
|
||||
alert(msg);
|
||||
}
|
||||
|
||||
function ok(p, msg) {
|
||||
if (p)
|
||||
sendMessage("OK: " + msg);
|
||||
else
|
||||
sendMessage("KO: " + msg);
|
||||
}
|
||||
|
||||
function testXHR(file, data_head, mapped, cb) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', file);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onreadystatechange = function xhrReadystatechange() {
|
||||
if (xhr.readyState !== xhr.DONE) {
|
||||
return;
|
||||
}
|
||||
if (xhr.status && xhr.status == 200) {
|
||||
var ct = xhr.getResponseHeader("Content-Type");
|
||||
if (mapped) {
|
||||
ok(ct.indexOf("mem-mapped") != -1, "Data is memory-mapped");
|
||||
} else {
|
||||
ok(ct.indexOf("mem-mapped") == -1, "Data is not memory-mapped");
|
||||
}
|
||||
var data = xhr.response;
|
||||
ok(data, "Data is non-null");
|
||||
var str = String.fromCharCode.apply(null, Uint8Array(data));
|
||||
ok(str.length == data_head.length + gPaddingSize, "Data size is correct");
|
||||
ok(str.slice(0, data_head.length) == data_head, "Data head is correct");
|
||||
ok(str.slice(data_head.length) == gPadding, "Data padding is correct");
|
||||
cb();
|
||||
} else {
|
||||
ok(false, "XHR error: " + xhr.status + " - " + xhr.statusText + "\n");
|
||||
cb();
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
// Memory-mapped array buffer.
|
||||
function test_mapped() {
|
||||
testXHR('data_1.txt', gData1, true, runTests);
|
||||
}
|
||||
|
||||
// Non memory-mapped array buffer.
|
||||
function test_non_mapped() {
|
||||
// Make sure array buffer retrieved from compressed file in package is
|
||||
// handled by memory allocation instead of memory mapping.
|
||||
testXHR('data_2.txt', gData2, false, runTests);
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_mapped,
|
||||
test_non_mapped
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
sendMessage("DONE");
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
function go() {
|
||||
ok(true, "Launched app");
|
||||
runTests();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go();">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,115 @@
|
|||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
// From prio.h
|
||||
const PR_RDWR = 0x04;
|
||||
const PR_CREATE_FILE = 0x08;
|
||||
const PR_TRUNCATE = 0x20;
|
||||
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
var gBasePath = "chrome/dom/apps/tests/";
|
||||
var gAppPath = gBasePath + "file_bug_945152.html";
|
||||
var gData1 = "TEST_DATA_1:ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
var gData2 = "TEST_DATA_2:1234567890";
|
||||
var gPaddingChar = '.';
|
||||
var gPaddingSize = 10000;
|
||||
var gPackageName = "file_bug_945152.zip";
|
||||
var gManifestData = {
|
||||
name: "Testing app",
|
||||
description: "Testing app",
|
||||
package_path: "http://test/chrome/dom/apps/tests/file_bug_945152.sjs?getPackage=1",
|
||||
launch_path: "/index.html",
|
||||
developer: {
|
||||
name: "devname",
|
||||
url: "http://dev.url"
|
||||
},
|
||||
default_locale: "en-US"
|
||||
};
|
||||
|
||||
function handleRequest(request, response) {
|
||||
var query = getQuery(request);
|
||||
|
||||
// Create the packaged app.
|
||||
if ("createApp" in query) {
|
||||
var zipWriter = Cc["@mozilla.org/zipwriter;1"]
|
||||
.createInstance(Ci.nsIZipWriter);
|
||||
var zipFile = FileUtils.getFile("TmpD", [gPackageName]);
|
||||
zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
|
||||
|
||||
var manifest = JSON.stringify(gManifestData);
|
||||
addZipEntry(zipWriter, manifest, "manifest.webapp", Ci.nsIZipWriter.COMPRESSION_BEST);
|
||||
var app = readFile(gAppPath, false);
|
||||
addZipEntry(zipWriter, app, "index.html", Ci.nsIZipWriter.COMPRESSION_BEST);
|
||||
var padding = "";
|
||||
for (var i = 0; i < gPaddingSize; i++) {
|
||||
padding += gPaddingChar;
|
||||
}
|
||||
var data = gData1 + padding;
|
||||
addZipEntry(zipWriter, data, "data_1.txt", Ci.nsIZipWriter.COMPRESSION_NONE);
|
||||
data = gData2 + padding;
|
||||
addZipEntry(zipWriter, data, "data_2.txt", Ci.nsIZipWriter.COMPRESSION_BEST);
|
||||
|
||||
zipWriter.alignStoredFiles(4096);
|
||||
zipWriter.close();
|
||||
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.write("OK");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're generating a webapp manifest.
|
||||
if ("getManifest" in query) {
|
||||
response.setHeader("Content-Type", "application/x-web-app-manifest+json", false);
|
||||
response.write(JSON.stringify(gManifestData));
|
||||
return;
|
||||
}
|
||||
|
||||
// Serve the application package.
|
||||
if ("getPackage" in query) {
|
||||
var resource = readFile(gPackageName, true);
|
||||
response.setHeader("Content-Type",
|
||||
"Content-Type: application/java-archive", false);
|
||||
response.write(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
response.setHeader("Content-type", "text-html", false);
|
||||
response.write("KO");
|
||||
}
|
||||
|
||||
function getQuery(request) {
|
||||
var query = {};
|
||||
request.queryString.split('&').forEach(function (val) {
|
||||
var [name, value] = val.split('=');
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
return query;
|
||||
}
|
||||
|
||||
// File and resources helpers
|
||||
|
||||
function addZipEntry(zipWriter, entry, entryName, compression) {
|
||||
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stream.setData(entry, entry.length);
|
||||
zipWriter.addEntryStream(entryName, Date.now(),
|
||||
compression, stream, false);
|
||||
}
|
||||
|
||||
function readFile(path, fromTmp) {
|
||||
var dir = fromTmp ? "TmpD" : "CurWorkD";
|
||||
var file = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get(dir, Ci.nsILocalFile);
|
||||
var fstream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
var split = path.split("/");
|
||||
for(var i = 0; i < split.length; ++i) {
|
||||
file.append(split[i]);
|
||||
}
|
||||
fstream.init(file, -1, 0, 0);
|
||||
var data = NetUtil.readInputStreamToString(fstream, fstream.available());
|
||||
fstream.close();
|
||||
return data;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=945152
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 945152</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const gBaseURL = 'http://test/chrome/dom/apps/tests/';
|
||||
const gSJS = gBaseURL + 'file_bug_945152.sjs';
|
||||
var gGenerator = runTest();
|
||||
|
||||
// When using SpecialPowers.autoConfirmAppInstall, it skips the local
|
||||
// installation, and we need this mock to get correct package path.
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/WebappOSUtils.jsm");
|
||||
var oldWebappOSUtils = WebappOSUtils;
|
||||
WebappOSUtils.getPackagePath = function(aApp) {
|
||||
return aApp.basePath + "/" + aApp.id;
|
||||
}
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
WebappOSUtils = oldWebappOSUtils;
|
||||
});
|
||||
|
||||
function go() {
|
||||
gGenerator.next();
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
try { gGenerator.next(); }
|
||||
catch (e) { dump("Got exception: " + e + "\n"); }
|
||||
}
|
||||
|
||||
function mozAppsError() {
|
||||
ok(false, "mozApps error: " + this.error.name);
|
||||
finish();
|
||||
}
|
||||
|
||||
function xhrError(event, url) {
|
||||
var xhr = event.target;
|
||||
ok(false, "XHR error loading " + url + ": " + xhr.status + " - " +
|
||||
xhr.statusText);
|
||||
finish();
|
||||
}
|
||||
|
||||
function xhrAbort(url) {
|
||||
ok(false, "XHR abort loading " + url);
|
||||
finish();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// Set up.
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["dom.mozBrowserFramesEnabled", true],
|
||||
["dom.mapped_arraybuffer.enabled", true]
|
||||
]
|
||||
}, continueTest);
|
||||
yield undefined;
|
||||
|
||||
SpecialPowers.autoConfirmAppInstall(continueTest);
|
||||
yield undefined;
|
||||
|
||||
// Create app on server side.
|
||||
createApp(continueTest);
|
||||
yield undefined;
|
||||
|
||||
// Install app.
|
||||
var app;
|
||||
navigator.mozApps.mgmt.oninstall = function(e) {
|
||||
ok(true, "Got oninstall event");
|
||||
app = e.application;
|
||||
app.ondownloaderror = mozAppsError;
|
||||
app.ondownloadsuccess = continueTest;
|
||||
};
|
||||
|
||||
var req = navigator.mozApps.installPackage(gSJS + '?getManifest=1');
|
||||
req.onerror = mozAppsError;
|
||||
req.onsuccess = function() {
|
||||
ok(true, "Application installed");
|
||||
};
|
||||
yield undefined;
|
||||
|
||||
// Launch app.
|
||||
launchApp(app, continueTest);
|
||||
yield undefined;
|
||||
|
||||
// Uninstall app.
|
||||
var req = navigator.mozApps.mgmt.uninstall(app);
|
||||
req.onerror = mozAppsError;
|
||||
req.onsuccess = continueTest;
|
||||
yield undefined;
|
||||
|
||||
// All done.
|
||||
ok(true, "All done");
|
||||
finish();
|
||||
}
|
||||
|
||||
function createApp(cb) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var url = gSJS + '?createApp=1';
|
||||
xhr.addEventListener("load", function() { is(xhr.responseText, "OK", "createApp OK"); cb(); });
|
||||
xhr.addEventListener("error", event => xhrError(event, url));
|
||||
xhr.addEventListener("abort", event => xhrAbort(url));
|
||||
xhr.open('GET', url, true);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function launchApp(app, cb) {
|
||||
// Set up the app.
|
||||
var ifr = document.createElement('iframe');
|
||||
ifr.setAttribute('mozbrowser', 'true');
|
||||
ifr.setAttribute('mozapp', app.manifestURL);
|
||||
ifr.setAttribute('src', app.origin + app.manifest.launch_path);
|
||||
var domParent = document.getElementById('container');
|
||||
|
||||
// Set us up to listen for messages from the app.
|
||||
var listener = function(e) {
|
||||
var message = e.detail.message;
|
||||
if (/OK/.exec(message)) {
|
||||
ok(true, "Message from app: " + message);
|
||||
} else if (/KO/.exec(message)) {
|
||||
ok(false, "Message from app: " + message);
|
||||
} else if (/DONE/.exec(message)) {
|
||||
ok(true, "Messaging from app complete");
|
||||
ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
|
||||
domParent.removeChild(ifr);
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
// This event is triggered when the app calls "alert".
|
||||
ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
|
||||
|
||||
// Add the iframe to the DOM, triggering the launch.
|
||||
domParent.appendChild(ifr);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=945152">Mozilla Bug 945152</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -18,8 +18,8 @@
|
|||
});
|
||||
|
||||
var test;
|
||||
runTest(function () {
|
||||
test = new PeerConnectionTest();
|
||||
runTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
test.setMediaConstraints([{audio: true}, {video: true}],
|
||||
[{audio: true}, {video: true}]);
|
||||
test.run();
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
});
|
||||
|
||||
var test;
|
||||
runTest(function () {
|
||||
test = new PeerConnectionTest();
|
||||
runTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
test.setMediaConstraints([{audio: true, video: true}],
|
||||
[{audio: true, video: true}]);
|
||||
test.run();
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
});
|
||||
|
||||
var test;
|
||||
runTest(function () {
|
||||
test = new PeerConnectionTest();
|
||||
runTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
test.setMediaConstraints([{video: true}], [{video: true}]);
|
||||
test.run();
|
||||
});
|
||||
|
|
|
@ -5,6 +5,8 @@ let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Prom
|
|||
let telephony;
|
||||
let conference;
|
||||
|
||||
const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
|
||||
|
||||
/**
|
||||
* Emulator helper.
|
||||
*/
|
||||
|
@ -1080,9 +1082,18 @@ function _startTest(permissions, test) {
|
|||
}
|
||||
}
|
||||
|
||||
let debugPref;
|
||||
|
||||
function setUp() {
|
||||
log("== Test SetUp ==");
|
||||
|
||||
// Turn on debugging pref.
|
||||
debugPref = SpecialPowers.getBoolPref(kPrefRilDebuggingEnabled);
|
||||
SpecialPowers.setBoolPref(kPrefRilDebuggingEnabled, true);
|
||||
log("Set debugging pref: " + debugPref + " => true");
|
||||
|
||||
permissionSetUp();
|
||||
|
||||
// Make sure that we get the telephony after adding permission.
|
||||
telephony = window.navigator.mozTelephony;
|
||||
ok(telephony);
|
||||
|
@ -1100,7 +1111,13 @@ function _startTest(permissions, test) {
|
|||
log("== Test TearDown ==");
|
||||
restoreTelephonyDial();
|
||||
emulator.waitFinish()
|
||||
.then(permissionTearDown)
|
||||
.then(() => {
|
||||
permissionTearDown();
|
||||
|
||||
// Restore debugging pref.
|
||||
SpecialPowers.setBoolPref(kPrefRilDebuggingEnabled, debugPref);
|
||||
log("Set debugging pref: true => " + debugPref);
|
||||
})
|
||||
.then(function() {
|
||||
originalFinish.apply(this, arguments);
|
||||
});
|
||||
|
|
|
@ -12,9 +12,12 @@
|
|||
|
||||
interface AudioProcessingEvent : Event {
|
||||
|
||||
readonly attribute double playbackTime;
|
||||
readonly attribute AudioBuffer inputBuffer;
|
||||
readonly attribute AudioBuffer outputBuffer;
|
||||
readonly attribute double playbackTime;
|
||||
|
||||
[Throws]
|
||||
readonly attribute AudioBuffer inputBuffer;
|
||||
[Throws]
|
||||
readonly attribute AudioBuffer outputBuffer;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -340,6 +340,9 @@ LoadRuntimeAndContextOptions(const char* aPrefName, void* /* aClosure */)
|
|||
if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion"))) {
|
||||
runtimeOptions.setIon(true);
|
||||
}
|
||||
if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp"))) {
|
||||
runtimeOptions.setNativeRegExp(true);
|
||||
}
|
||||
|
||||
// Common options.
|
||||
JS::ContextOptions commonContextOptions = kRequiredContextOptions;
|
||||
|
|
|
@ -920,7 +920,8 @@ static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode, bool aIs
|
|||
dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(0, "Unsupported blend mode!");
|
||||
MOZ_ASSERT_UNREACHABLE("Unsupported blend mode!");
|
||||
return false;
|
||||
}
|
||||
|
||||
aGL->fBlendFuncSeparate(srcBlend, dstBlend,
|
||||
|
|
|
@ -24,7 +24,8 @@ class JSFreeOp;
|
|||
|
||||
namespace js {
|
||||
class InterpreterFrame;
|
||||
class ScriptFrameIter;
|
||||
class FrameIter;
|
||||
class ScriptSource;
|
||||
}
|
||||
|
||||
// Raw JSScript* because this needs to be callable from a signal handler.
|
||||
|
@ -39,18 +40,20 @@ namespace JS {
|
|||
class FrameDescription
|
||||
{
|
||||
public:
|
||||
explicit FrameDescription(const js::ScriptFrameIter& iter);
|
||||
explicit FrameDescription(const js::FrameIter& iter);
|
||||
FrameDescription(const FrameDescription &rhs);
|
||||
~FrameDescription();
|
||||
|
||||
unsigned lineno() {
|
||||
if (!linenoComputed) {
|
||||
if (!linenoComputed_) {
|
||||
lineno_ = JS_PCToLineNumber(nullptr, script_, pc_);
|
||||
linenoComputed = true;
|
||||
linenoComputed_ = true;
|
||||
}
|
||||
return lineno_;
|
||||
}
|
||||
|
||||
const char *filename() const {
|
||||
return JS_GetScriptFilename(script_);
|
||||
return filename_;
|
||||
}
|
||||
|
||||
JSFlatString *funDisplayName() const {
|
||||
|
@ -67,11 +70,22 @@ class FrameDescription
|
|||
}
|
||||
|
||||
private:
|
||||
Heap<JSScript*> script_;
|
||||
void operator=(const FrameDescription &) MOZ_DELETE;
|
||||
|
||||
// These fields are always initialized:
|
||||
Heap<JSString*> funDisplayName_;
|
||||
jsbytecode *pc_;
|
||||
const char *filename_;
|
||||
|
||||
// One of script_ xor scriptSource_ is non-null.
|
||||
Heap<JSScript*> script_;
|
||||
js::ScriptSource *scriptSource_;
|
||||
|
||||
// For script_-having frames, lineno_ is lazily computed as an optimization.
|
||||
bool linenoComputed_;
|
||||
unsigned lineno_;
|
||||
bool linenoComputed;
|
||||
|
||||
// pc_ is non-null iff script_ is non-null. If !pc_, linenoComputed_ = true.
|
||||
jsbytecode *pc_;
|
||||
};
|
||||
|
||||
struct StackDescription
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#ifndef JS_YARR
|
||||
#include "irregexp/RegExpParser.h"
|
||||
#endif
|
||||
|
||||
#include "vm/RegExpStatics.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
|
@ -94,11 +98,15 @@ ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re,
|
|||
|
||||
/* Switch between MatchOnly and IncludeSubpatterns modes. */
|
||||
if (matches.isPair) {
|
||||
#ifdef JS_YARR
|
||||
size_t lastIndex_orig = *lastIndex;
|
||||
/* Only one MatchPair slot provided: execute short-circuiting regexp. */
|
||||
status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair);
|
||||
if (status == RegExpRunStatus_Success && res)
|
||||
res->updateLazily(cx, input, &re, lastIndex_orig);
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
} else {
|
||||
/* Vector of MatchPairs provided: execute full regexp. */
|
||||
status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs);
|
||||
|
@ -107,7 +115,6 @@ ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re,
|
|||
return RegExpRunStatus_Error;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -283,8 +290,16 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
|
|||
if (!escapedSourceStr)
|
||||
return false;
|
||||
|
||||
if (!js::RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr))
|
||||
#ifdef JS_YARR
|
||||
if (!RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr))
|
||||
return false;
|
||||
#else // JS_YARR
|
||||
CompileOptions options(cx);
|
||||
frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
|
||||
|
||||
if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), escapedSourceStr->chars(), escapedSourceStr->length()))
|
||||
return false;
|
||||
#endif // JS_YARR
|
||||
|
||||
RegExpStatics *res = cx->global()->getRegExpStatics(cx);
|
||||
if (!res)
|
||||
|
@ -678,8 +693,13 @@ js::regexp_exec_no_statics(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
regexp_test_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
#ifdef JS_YARR
|
||||
MatchPair match;
|
||||
MatchConduit conduit(&match);
|
||||
#else
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
MatchConduit conduit(&matches);
|
||||
#endif
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, args, conduit);
|
||||
args.rval().setBoolean(status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
|
@ -689,8 +709,13 @@ regexp_test_impl(JSContext *cx, CallArgs args)
|
|||
bool
|
||||
js::regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, bool *result)
|
||||
{
|
||||
#ifdef JS_YARR
|
||||
MatchPair match;
|
||||
MatchConduit conduit(&match);
|
||||
#else
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
MatchConduit conduit(&matches);
|
||||
#endif
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, conduit, UpdateRegExpStatics);
|
||||
*result = (status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
|
@ -714,8 +739,13 @@ js::regexp_test_no_statics(JSContext *cx, unsigned argc, Value *vp)
|
|||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
|
||||
#ifdef JS_YARR
|
||||
MatchPair match;
|
||||
MatchConduit conduit(&match);
|
||||
#else
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
MatchConduit conduit(&matches);
|
||||
#endif
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, DontUpdateRegExpStatics);
|
||||
args.rval().setBoolean(status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
|
|
|
@ -19,6 +19,8 @@ js_InitRegExpClass(JSContext *cx, js::HandleObject obj);
|
|||
|
||||
namespace js {
|
||||
|
||||
class MatchConduit;
|
||||
|
||||
// Whether RegExp statics should be updated with the input and results of a
|
||||
// regular expression execution.
|
||||
enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
|
||||
|
|
|
@ -1606,6 +1606,22 @@ DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool
|
||||
DumpObject(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject obj(cx);
|
||||
if (!JS_ConvertArguments(cx, args, "o", obj.address()))
|
||||
return false;
|
||||
|
||||
js_DumpObject(obj);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
JS_FN_HELP("gc", ::GC, 0, 0,
|
||||
"gc([obj] | 'compartment')",
|
||||
|
@ -1875,6 +1891,13 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
|||
JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
|
||||
"stopTraceLogger()",
|
||||
" Stop logging the mainThread."),
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FN_HELP("dumpObject", DumpObject, 1, 0,
|
||||
"dumpObject()",
|
||||
" Dump an internal representation of an object."),
|
||||
#endif
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
|
|
|
@ -1977,19 +1977,16 @@ dnl Configure JIT support
|
|||
case "$target" in
|
||||
i?86-*)
|
||||
ENABLE_ION=1
|
||||
ENABLE_YARR_JIT=1
|
||||
AC_DEFINE(JS_CPU_X86)
|
||||
AC_DEFINE(JS_NUNBOX32)
|
||||
;;
|
||||
x86_64*-*)
|
||||
ENABLE_ION=1
|
||||
ENABLE_YARR_JIT=1
|
||||
AC_DEFINE(JS_CPU_X64)
|
||||
AC_DEFINE(JS_PUNBOX64)
|
||||
;;
|
||||
arm*-*)
|
||||
ENABLE_ION=1
|
||||
ENABLE_YARR_JIT=1
|
||||
AC_DEFINE(JS_CPU_ARM)
|
||||
AC_DEFINE(JS_NUNBOX32)
|
||||
;;
|
||||
|
@ -2010,10 +2007,6 @@ MOZ_ARG_DISABLE_BOOL(ion,
|
|||
[ --disable-ion Disable use of the IonMonkey JIT],
|
||||
ENABLE_ION= )
|
||||
|
||||
MOZ_ARG_DISABLE_BOOL(yarr-jit,
|
||||
[ --disable-yarr-jit Disable YARR JIT support],
|
||||
ENABLE_YARR_JIT= )
|
||||
|
||||
AC_SUBST(ENABLE_METHODJIT_SPEW)
|
||||
|
||||
AC_SUBST(ENABLE_ION)
|
||||
|
@ -2022,12 +2015,6 @@ if test "$ENABLE_ION"; then
|
|||
AC_DEFINE(JS_ION)
|
||||
fi
|
||||
|
||||
AC_SUBST(ENABLE_YARR_JIT)
|
||||
|
||||
if test "$ENABLE_YARR_JIT"; then
|
||||
AC_DEFINE(ENABLE_YARR_JIT)
|
||||
fi
|
||||
|
||||
if test -n "$COMPILE_ENVIRONMENT"; then
|
||||
MOZ_COMPILER_OPTS
|
||||
fi
|
||||
|
@ -2994,6 +2981,20 @@ if test "$ENABLE_TRACE_LOGGING"; then
|
|||
AC_DEFINE(JS_TRACE_LOGGING)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable yarr regexp engine
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(yarr,
|
||||
[ --enable-yarr Enable yarr regexp engine],
|
||||
ENABLE_YARR=1,
|
||||
ENABLE_YARR= )
|
||||
|
||||
AC_SUBST(ENABLE_YARR)
|
||||
|
||||
if test "$ENABLE_YARR"; then
|
||||
AC_DEFINE(JS_YARR)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable any treating of compile warnings as errors
|
||||
dnl ========================================================
|
||||
|
|
|
@ -147,6 +147,9 @@ class BumpChunk
|
|||
|
||||
} // namespace detail
|
||||
|
||||
void
|
||||
CrashAtUnhandlableOOM(const char *reason);
|
||||
|
||||
// LIFO bump allocator: used for phase-oriented and fast LIFO allocations.
|
||||
//
|
||||
// Note: |latest| is not necessary "last". We leave BumpChunks latent in the
|
||||
|
@ -269,6 +272,14 @@ class LifoAlloc
|
|||
return result;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void *allocInfallible(size_t n) {
|
||||
if (void *result = alloc(n))
|
||||
return result;
|
||||
CrashAtUnhandlableOOM("LifoAlloc::allocInfallible");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensures that enough space exists to satisfy N bytes worth of
|
||||
// allocation requests, not necessarily contiguous. Note that this does
|
||||
// not guarantee a successful single allocation of N bytes.
|
||||
|
@ -385,6 +396,7 @@ class LifoAlloc
|
|||
}
|
||||
|
||||
JS_DECLARE_NEW_METHODS(new_, alloc, MOZ_ALWAYS_INLINE)
|
||||
JS_DECLARE_NEW_METHODS(newInfallible, allocInfallible, MOZ_ALWAYS_INLINE)
|
||||
|
||||
// A mutable enumeration of the allocated data.
|
||||
class Enum
|
||||
|
@ -490,6 +502,12 @@ class LifoAllocScope
|
|||
}
|
||||
};
|
||||
|
||||
enum Fallibility {
|
||||
Fallible,
|
||||
Infallible
|
||||
};
|
||||
|
||||
template <Fallibility fb>
|
||||
class LifoAllocPolicy
|
||||
{
|
||||
LifoAlloc &alloc_;
|
||||
|
@ -499,18 +517,19 @@ class LifoAllocPolicy
|
|||
: alloc_(alloc)
|
||||
{}
|
||||
void *malloc_(size_t bytes) {
|
||||
return alloc_.alloc(bytes);
|
||||
return fb == Fallible ? alloc_.alloc(bytes) : alloc_.allocInfallible(bytes);
|
||||
}
|
||||
void *calloc_(size_t bytes) {
|
||||
void *p = malloc_(bytes);
|
||||
if (p)
|
||||
memset(p, 0, bytes);
|
||||
if (fb == Fallible && !p)
|
||||
return nullptr;
|
||||
memset(p, 0, bytes);
|
||||
return p;
|
||||
}
|
||||
void *realloc_(void *p, size_t oldBytes, size_t bytes) {
|
||||
void *n = malloc_(bytes);
|
||||
if (!n)
|
||||
return n;
|
||||
if (fb == Fallible && !n)
|
||||
return nullptr;
|
||||
memcpy(n, p, Min(oldBytes, bytes));
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -6914,7 +6914,7 @@ Parser<ParseHandler>::newRegExp()
|
|||
if (!res)
|
||||
return null();
|
||||
|
||||
reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
|
||||
reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream, alloc);
|
||||
if (!reobj)
|
||||
return null();
|
||||
|
||||
|
|
|
@ -291,7 +291,7 @@ TokenStream::TokenStream(ExclusiveContext *cx, const ReadOnlyCompileOptions &opt
|
|||
// initial line's base must be included in the buffer. linebase and userbuf
|
||||
// were adjusted above, and if we are starting tokenization part way through
|
||||
// this line then adjust the next character.
|
||||
userbuf.setAddressOfNextRawChar(base);
|
||||
userbuf.setAddressOfNextRawChar(base, /* allowPoisoned = */ true);
|
||||
|
||||
// Nb: the following tables could be static, but initializing them here is
|
||||
// much easier. Don't worry, the time to initialize them for each
|
||||
|
@ -631,6 +631,17 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne
|
|||
err.report.column = srcCoords.columnIndex(offset);
|
||||
}
|
||||
|
||||
// If we have no location information, try to get one from the caller.
|
||||
if (offset != NoOffset && !err.report.filename && cx->isJSContext()) {
|
||||
NonBuiltinFrameIter iter(cx->asJSContext(),
|
||||
FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED,
|
||||
cx->compartment()->principals);
|
||||
if (!iter.done() && iter.scriptFilename()) {
|
||||
err.report.filename = iter.scriptFilename();
|
||||
err.report.lineno = iter.computeLine(&err.report.column);
|
||||
}
|
||||
}
|
||||
|
||||
err.argumentsType = (flags & JSREPORT_UC) ? ArgumentsAreUnicode : ArgumentsAreASCII;
|
||||
|
||||
if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, nullptr, errorNumber, &err.message,
|
||||
|
|
133
js/src/gc/Heap.h
133
js/src/gc/Heap.h
|
@ -148,15 +148,14 @@ const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD;
|
|||
*/
|
||||
class FreeSpan
|
||||
{
|
||||
friend class FreeList;
|
||||
friend class ArenaCellIterImpl;
|
||||
friend class CompactFreeSpan;
|
||||
friend class FreeList;
|
||||
|
||||
uintptr_t first;
|
||||
uintptr_t last;
|
||||
|
||||
public:
|
||||
FreeSpan() {}
|
||||
|
||||
// This inits just |first| and |last|; if the span is non-empty it doesn't
|
||||
// do anything with the next span stored at |last|.
|
||||
void initBoundsUnchecked(uintptr_t first, uintptr_t last) {
|
||||
|
@ -188,37 +187,6 @@ class FreeSpan
|
|||
checkSpan(thingSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* To minimize the size of the arena header the first span is encoded
|
||||
* there as offsets from the arena start.
|
||||
*/
|
||||
static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) {
|
||||
static_assert(ArenaShift < 16, "Check that we can pack offsets into uint16_t.");
|
||||
JS_ASSERT(firstOffset <= lastOffset);
|
||||
JS_ASSERT(lastOffset < ArenaSize);
|
||||
return firstOffset | (lastOffset << 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encoded offsets for a full arena, i.e. one with an empty FreeSpan.
|
||||
*/
|
||||
static const size_t FullArenaOffsets = 0;
|
||||
|
||||
static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) {
|
||||
JS_ASSERT(!(arenaAddr & ArenaMask));
|
||||
FreeSpan decodedSpan;
|
||||
if (offsets == FullArenaOffsets) {
|
||||
decodedSpan.initAsEmpty();
|
||||
} else {
|
||||
size_t firstOffset = offsets & 0xFFFF;
|
||||
size_t lastOffset = offsets >> 16;
|
||||
JS_ASSERT(firstOffset <= lastOffset);
|
||||
JS_ASSERT(lastOffset < ArenaSize);
|
||||
decodedSpan.initBounds(arenaAddr + firstOffset, arenaAddr + lastOffset);
|
||||
}
|
||||
return decodedSpan;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
checkSpan();
|
||||
return !first;
|
||||
|
@ -247,10 +215,6 @@ class FreeSpan
|
|||
}
|
||||
#endif
|
||||
|
||||
size_t encodeAsOffsets() const {
|
||||
return encodeOffsets(first & ArenaMask, last & ArenaMask);
|
||||
}
|
||||
|
||||
size_t length(size_t thingSize) const {
|
||||
checkSpan();
|
||||
JS_ASSERT((last - first) % thingSize == 0);
|
||||
|
@ -270,6 +234,7 @@ class FreeSpan
|
|||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
// Some callers can pass in |thingSize| easily, and we can do stronger
|
||||
// checking in that case.
|
||||
void checkSpan(size_t thingSize = 0) const {
|
||||
|
@ -300,6 +265,57 @@ class FreeSpan
|
|||
}
|
||||
};
|
||||
|
||||
class CompactFreeSpan
|
||||
{
|
||||
uint16_t firstOffset_;
|
||||
uint16_t lastOffset_;
|
||||
|
||||
public:
|
||||
CompactFreeSpan(size_t firstOffset, size_t lastOffset)
|
||||
: firstOffset_(firstOffset)
|
||||
, lastOffset_(lastOffset)
|
||||
{}
|
||||
|
||||
void initAsEmpty() {
|
||||
firstOffset_ = 0;
|
||||
lastOffset_ = 0;
|
||||
}
|
||||
|
||||
bool operator==(const CompactFreeSpan &other) const {
|
||||
return firstOffset_ == other.firstOffset_ &&
|
||||
lastOffset_ == other.lastOffset_;
|
||||
}
|
||||
|
||||
void compact(FreeSpan span) {
|
||||
if (span.isEmpty()) {
|
||||
initAsEmpty();
|
||||
} else {
|
||||
static_assert(ArenaShift < 16, "Check that we can pack offsets into uint16_t.");
|
||||
uintptr_t arenaAddr = span.arenaAddress();
|
||||
firstOffset_ = span.first - arenaAddr;
|
||||
lastOffset_ = span.last - arenaAddr;
|
||||
}
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
JS_ASSERT(!!firstOffset_ == !!lastOffset_);
|
||||
return !firstOffset_;
|
||||
}
|
||||
|
||||
FreeSpan decompact(uintptr_t arenaAddr) const {
|
||||
JS_ASSERT(!(arenaAddr & ArenaMask));
|
||||
FreeSpan decodedSpan;
|
||||
if (isEmpty()) {
|
||||
decodedSpan.initAsEmpty();
|
||||
} else {
|
||||
JS_ASSERT(firstOffset_ <= lastOffset_);
|
||||
JS_ASSERT(lastOffset_ < ArenaSize);
|
||||
decodedSpan.initBounds(arenaAddr + firstOffset_, arenaAddr + lastOffset_);
|
||||
}
|
||||
return decodedSpan;
|
||||
}
|
||||
};
|
||||
|
||||
class FreeList
|
||||
{
|
||||
// Although |head| is private, it is exposed to the JITs via the
|
||||
|
@ -379,22 +395,6 @@ class FreeList
|
|||
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
|
||||
return reinterpret_cast<void *>(thing);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate from a newly allocated arena. The arena will have been set up
|
||||
* as fully used during the initialization so to allocate we simply return
|
||||
* the first thing in the arena and set the free list to point to the
|
||||
* second.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset,
|
||||
size_t thingSize) {
|
||||
JS_ASSERT(isEmpty());
|
||||
JS_ASSERT(!(arenaAddr & ArenaMask));
|
||||
uintptr_t thing = arenaAddr + firstThingOffset;
|
||||
head.initFinal(thing + thingSize, arenaAddr + ArenaSize - thingSize, thingSize);
|
||||
JS_EXTRA_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
|
||||
return reinterpret_cast<void *>(thing);
|
||||
}
|
||||
};
|
||||
|
||||
/* Every arena has a header. */
|
||||
|
@ -411,11 +411,10 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
|
|||
|
||||
private:
|
||||
/*
|
||||
* The first span of free things in the arena. We encode it as the start
|
||||
* and end offsets within the arena, not as FreeSpan structure, to
|
||||
* minimize the header size.
|
||||
* The first span of free things in the arena. We encode it as a
|
||||
* CompactFreeSpan rather than a FreeSpan to minimize the header size.
|
||||
*/
|
||||
size_t firstFreeSpanOffsets;
|
||||
CompactFreeSpan firstFreeSpan;
|
||||
|
||||
/*
|
||||
* One of AllocKind constants or FINALIZE_LIMIT when the arena does not
|
||||
|
@ -479,8 +478,11 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
|
|||
static_assert(FINALIZE_LIMIT <= 255, "We must be able to fit the allockind into uint8_t.");
|
||||
allocKind = size_t(kind);
|
||||
|
||||
/* See comments in FreeSpan::allocateFromNewArena. */
|
||||
firstFreeSpanOffsets = FreeSpan::FullArenaOffsets;
|
||||
/*
|
||||
* The firstFreeSpan is initially marked as empty (and thus the arena
|
||||
* is marked as full). See allocateFromArenaInline().
|
||||
*/
|
||||
firstFreeSpan.initAsEmpty();
|
||||
}
|
||||
|
||||
void setAsNotAllocated() {
|
||||
|
@ -502,13 +504,13 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
|
|||
inline size_t getThingSize() const;
|
||||
|
||||
bool hasFreeThings() const {
|
||||
return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets;
|
||||
return !firstFreeSpan.isEmpty();
|
||||
}
|
||||
|
||||
inline bool isEmpty() const;
|
||||
|
||||
void setAsFullyUsed() {
|
||||
firstFreeSpanOffsets = FreeSpan::FullArenaOffsets;
|
||||
firstFreeSpan.initAsEmpty();
|
||||
}
|
||||
|
||||
inline FreeSpan getFirstFreeSpan() const;
|
||||
|
@ -916,7 +918,8 @@ ArenaHeader::isEmpty() const
|
|||
JS_ASSERT(allocated());
|
||||
size_t firstThingOffset = Arena::firstThingOffset(getAllocKind());
|
||||
size_t lastThingOffset = ArenaSize - getThingSize();
|
||||
return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, lastThingOffset);
|
||||
const CompactFreeSpan emptyCompactSpan(firstThingOffset, lastThingOffset);
|
||||
return firstFreeSpan == emptyCompactSpan;
|
||||
}
|
||||
|
||||
FreeSpan
|
||||
|
@ -925,14 +928,14 @@ ArenaHeader::getFirstFreeSpan() const
|
|||
#ifdef DEBUG
|
||||
checkSynchronizedWithFreeList();
|
||||
#endif
|
||||
return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
|
||||
return firstFreeSpan.decompact(arenaAddress());
|
||||
}
|
||||
|
||||
void
|
||||
ArenaHeader::setFirstFreeSpan(const FreeSpan *span)
|
||||
{
|
||||
JS_ASSERT_IF(!span->isEmpty(), span->isWithinArena(arenaAddress()));
|
||||
firstFreeSpanOffsets = span->encodeAsOffsets();
|
||||
firstFreeSpan.compact(*span);
|
||||
}
|
||||
|
||||
inline ArenaHeader *
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,224 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_NATIVE_REGEXP_MACRO_ASSEMBLER_H_
|
||||
#define V8_NATIVE_REGEXP_MACRO_ASSEMBLER_H_
|
||||
|
||||
#ifdef JS_ION
|
||||
|
||||
#include "irregexp/RegExpMacroAssembler.h"
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
struct InputOutputData
|
||||
{
|
||||
const jschar *inputStart;
|
||||
const jschar *inputEnd;
|
||||
|
||||
// Index into inputStart (in chars) at which to begin matching.
|
||||
size_t startIndex;
|
||||
|
||||
MatchPairs *matches;
|
||||
|
||||
// RegExpMacroAssembler::Result for non-global regexps, number of captures
|
||||
// for global regexps.
|
||||
int32_t result;
|
||||
|
||||
InputOutputData(const jschar *inputStart, const jschar *inputEnd,
|
||||
size_t startIndex, MatchPairs *matches)
|
||||
: inputStart(inputStart),
|
||||
inputEnd(inputEnd),
|
||||
startIndex(startIndex),
|
||||
matches(matches),
|
||||
result(0)
|
||||
{}
|
||||
};
|
||||
|
||||
struct FrameData
|
||||
{
|
||||
// Copy of the input/output data's data.
|
||||
jschar *inputStart;
|
||||
size_t startIndex;
|
||||
|
||||
// Pointer to the character before the input start.
|
||||
jschar *inputStartMinusOne;
|
||||
|
||||
// Copy of the input MatchPairs registers, may be modified by JIT code.
|
||||
int32_t *outputRegisters;
|
||||
int32_t numOutputRegisters;
|
||||
|
||||
int32_t successfulCaptures;
|
||||
|
||||
void *backtrackStackBase;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS NativeRegExpMacroAssembler : public RegExpMacroAssembler
|
||||
{
|
||||
public:
|
||||
// Type of input string to generate code for.
|
||||
enum Mode { ASCII = 1, JSCHAR = 2 };
|
||||
|
||||
NativeRegExpMacroAssembler(LifoAlloc *alloc, RegExpShared *shared,
|
||||
JSRuntime *rt, Mode mode, int registers_to_save);
|
||||
|
||||
// Inherited virtual methods.
|
||||
RegExpCode GenerateCode(JSContext *cx);
|
||||
int stack_limit_slack();
|
||||
bool CanReadUnaligned();
|
||||
void AdvanceCurrentPosition(int by);
|
||||
void AdvanceRegister(int reg, int by);
|
||||
void Backtrack();
|
||||
void Bind(jit::Label* label);
|
||||
void CheckAtStart(jit::Label* on_at_start);
|
||||
void CheckCharacter(unsigned c, jit::Label* on_equal);
|
||||
void CheckCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_equal);
|
||||
void CheckCharacterGT(jschar limit, jit::Label* on_greater);
|
||||
void CheckCharacterLT(jschar limit, jit::Label* on_less);
|
||||
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
|
||||
void CheckNotAtStart(jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterMinusAnd(jschar c, jschar minus, jschar and_with,
|
||||
jit::Label* on_not_equal);
|
||||
void CheckCharacterInRange(jschar from, jschar to,
|
||||
jit::Label* on_in_range);
|
||||
void CheckCharacterNotInRange(jschar from, jschar to,
|
||||
jit::Label* on_not_in_range);
|
||||
void CheckBitInTable(uint8_t *table, jit::Label* on_bit_set);
|
||||
void CheckPosition(int cp_offset, jit::Label* on_outside_input);
|
||||
void JumpOrBacktrack(jit::Label *to);
|
||||
bool CheckSpecialCharacterClass(jschar type, jit::Label* on_no_match);
|
||||
void Fail();
|
||||
void IfRegisterGE(int reg, int comparand, jit::Label* if_ge);
|
||||
void IfRegisterLT(int reg, int comparand, jit::Label* if_lt);
|
||||
void IfRegisterEqPos(int reg, jit::Label* if_eq);
|
||||
void LoadCurrentCharacter(int cp_offset, jit::Label* on_end_of_input,
|
||||
bool check_bounds = true, int characters = 1);
|
||||
void PopCurrentPosition();
|
||||
void PopRegister(int register_index);
|
||||
void PushCurrentPosition();
|
||||
void PushRegister(int register_index, StackCheckFlag check_stack_limit);
|
||||
void ReadCurrentPositionFromRegister(int reg);
|
||||
void ReadBacktrackStackPointerFromRegister(int reg);
|
||||
void SetCurrentPositionFromEnd(int by);
|
||||
void SetRegister(int register_index, int to);
|
||||
bool Succeed();
|
||||
void WriteCurrentPositionToRegister(int reg, int cp_offset);
|
||||
void ClearRegisters(int reg_from, int reg_to);
|
||||
void WriteBacktrackStackPointerToRegister(int reg);
|
||||
void PushBacktrack(jit::Label *label);
|
||||
void BindBacktrack(jit::Label *label);
|
||||
|
||||
// Compares two-byte strings case insensitively.
|
||||
// Called from generated RegExp code.
|
||||
static int CaseInsensitiveCompareUC16(jit::Address byte_offset1,
|
||||
jit::Address byte_offset2,
|
||||
size_t byte_length);
|
||||
|
||||
// Byte map of one byte characters with a 0xff if the character is a word
|
||||
// character (digit, letter or underscore) and 0x00 otherwise.
|
||||
// Used by generated RegExp code.
|
||||
static const uint8_t word_character_map[256];
|
||||
|
||||
// Byte size of chars in the string to match (decided by the Mode argument)
|
||||
inline int char_size() { return static_cast<int>(mode_); }
|
||||
inline jit::Scale factor() { return mode_ == JSCHAR ? jit::TimesTwo : jit::TimesOne; }
|
||||
|
||||
void BranchOrBacktrack(jit::Assembler::Condition condition, jit::Label *to);
|
||||
|
||||
// Pushes a register or constant on the backtrack stack. Decrements the
|
||||
// stack pointer by a word size and stores the register's value there.
|
||||
void PushBacktrack(jit::Register value);
|
||||
void PushBacktrack(int32_t value);
|
||||
|
||||
// Pop a value from the backtrack stack.
|
||||
void PopBacktrack(jit::Register target);
|
||||
|
||||
// Check whether we are exceeding the stack limit on the backtrack stack.
|
||||
void CheckBacktrackStackLimit();
|
||||
|
||||
void LoadCurrentCharacterUnchecked(int cp_offset, int characters);
|
||||
|
||||
private:
|
||||
jit::MacroAssembler masm;
|
||||
|
||||
JSRuntime *runtime;
|
||||
Mode mode_;
|
||||
jit::Label entry_label_;
|
||||
jit::Label start_label_;
|
||||
jit::Label backtrack_label_;
|
||||
jit::Label success_label_;
|
||||
jit::Label exit_label_;
|
||||
jit::Label stack_overflow_label_;
|
||||
jit::Label exit_with_exception_label_;
|
||||
|
||||
jit::GeneralRegisterSet savedNonVolatileRegisters;
|
||||
|
||||
struct LabelPatch {
|
||||
// Once it is bound via BindBacktrack, |label| becomes null and
|
||||
// |labelOffset| is set.
|
||||
jit::Label *label;
|
||||
size_t labelOffset;
|
||||
|
||||
jit::CodeOffsetLabel patchOffset;
|
||||
|
||||
LabelPatch(jit::Label *label, jit::CodeOffsetLabel patchOffset)
|
||||
: label(label), labelOffset(0), patchOffset(patchOffset)
|
||||
{}
|
||||
};
|
||||
|
||||
Vector<LabelPatch, 4, SystemAllocPolicy> labelPatches;
|
||||
|
||||
// See RegExpMacroAssembler.cpp for the meaning of these registers.
|
||||
jit::Register input_end_pointer;
|
||||
jit::Register current_character;
|
||||
jit::Register current_position;
|
||||
jit::Register backtrack_stack_pointer;
|
||||
jit::Register temp0, temp1, temp2;
|
||||
|
||||
// The frame_pointer-relative location of a regexp register.
|
||||
jit::Address register_location(int register_index) {
|
||||
checkRegister(register_index);
|
||||
return jit::Address(jit::StackPointer, register_offset(register_index));
|
||||
}
|
||||
|
||||
int32_t register_offset(int register_index) {
|
||||
return sizeof(FrameData) + register_index * sizeof(void *);
|
||||
}
|
||||
};
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // JS_ION
|
||||
|
||||
#endif // V8_NATIVE_REGEXP_MACRO_ASSEMBLER_H_
|
|
@ -0,0 +1,265 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "irregexp/RegExpAST.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::irregexp;
|
||||
|
||||
#define MAKE_ACCEPT(Name) \
|
||||
void* RegExp##Name::Accept(RegExpVisitor* visitor, void* data) { \
|
||||
return visitor->Visit##Name(this, data); \
|
||||
}
|
||||
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ACCEPT)
|
||||
#undef MAKE_ACCEPT
|
||||
|
||||
#define MAKE_TYPE_CASE(Name) \
|
||||
RegExp##Name* RegExpTree::As##Name() { \
|
||||
return nullptr; \
|
||||
} \
|
||||
bool RegExpTree::Is##Name() { return false; }
|
||||
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
|
||||
#undef MAKE_TYPE_CASE
|
||||
|
||||
#define MAKE_TYPE_CASE(Name) \
|
||||
RegExp##Name* RegExp##Name::As##Name() { \
|
||||
return this; \
|
||||
} \
|
||||
bool RegExp##Name::Is##Name() { return true; }
|
||||
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
|
||||
#undef MAKE_TYPE_CASE
|
||||
|
||||
static Interval
|
||||
ListCaptureRegisters(const RegExpTreeVector &children)
|
||||
{
|
||||
Interval result = Interval::Empty();
|
||||
for (size_t i = 0; i < children.length(); i++)
|
||||
result = result.Union(children[i]->CaptureRegisters());
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpDisjunction
|
||||
|
||||
RegExpDisjunction::RegExpDisjunction(RegExpTreeVector *alternatives)
|
||||
: alternatives_(alternatives)
|
||||
{
|
||||
JS_ASSERT(alternatives->length() > 1);
|
||||
RegExpTree* first_alternative = (*alternatives)[0];
|
||||
min_match_ = first_alternative->min_match();
|
||||
max_match_ = first_alternative->max_match();
|
||||
for (size_t i = 1; i < alternatives->length(); i++) {
|
||||
RegExpTree* alternative = (*alternatives)[i];
|
||||
min_match_ = Min(min_match_, alternative->min_match());
|
||||
max_match_ = Max(max_match_, alternative->max_match());
|
||||
}
|
||||
}
|
||||
|
||||
Interval
|
||||
RegExpDisjunction::CaptureRegisters()
|
||||
{
|
||||
return ListCaptureRegisters(alternatives());
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpDisjunction::IsAnchoredAtStart()
|
||||
{
|
||||
const RegExpTreeVector &alternatives = this->alternatives();
|
||||
for (size_t i = 0; i < alternatives.length(); i++) {
|
||||
if (!alternatives[i]->IsAnchoredAtStart())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpDisjunction::IsAnchoredAtEnd()
|
||||
{
|
||||
const RegExpTreeVector &alternatives = this->alternatives();
|
||||
for (size_t i = 0; i < alternatives.length(); i++) {
|
||||
if (!alternatives[i]->IsAnchoredAtEnd())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpAlternative
|
||||
|
||||
static int IncreaseBy(int previous, int increase)
|
||||
{
|
||||
if (RegExpTree::kInfinity - previous < increase)
|
||||
return RegExpTree::kInfinity;
|
||||
return previous + increase;
|
||||
}
|
||||
|
||||
RegExpAlternative::RegExpAlternative(RegExpTreeVector *nodes)
|
||||
: nodes_(nodes),
|
||||
min_match_(0),
|
||||
max_match_(0)
|
||||
{
|
||||
JS_ASSERT(nodes->length() > 1);
|
||||
for (size_t i = 0; i < nodes->length(); i++) {
|
||||
RegExpTree* node = (*nodes)[i];
|
||||
int node_min_match = node->min_match();
|
||||
min_match_ = IncreaseBy(min_match_, node_min_match);
|
||||
int node_max_match = node->max_match();
|
||||
max_match_ = IncreaseBy(max_match_, node_max_match);
|
||||
}
|
||||
}
|
||||
|
||||
Interval
|
||||
RegExpAlternative::CaptureRegisters()
|
||||
{
|
||||
return ListCaptureRegisters(nodes());
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpAlternative::IsAnchoredAtStart()
|
||||
{
|
||||
const RegExpTreeVector &nodes = this->nodes();
|
||||
for (size_t i = 0; i < nodes.length(); i++) {
|
||||
RegExpTree *node = nodes[i];
|
||||
if (node->IsAnchoredAtStart()) { return true; }
|
||||
if (node->max_match() > 0) { return false; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpAlternative::IsAnchoredAtEnd()
|
||||
{
|
||||
const RegExpTreeVector &nodes = this->nodes();
|
||||
for (int i = nodes.length() - 1; i >= 0; i--) {
|
||||
RegExpTree *node = nodes[i];
|
||||
if (node->IsAnchoredAtEnd()) { return true; }
|
||||
if (node->max_match() > 0) { return false; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpAssertion
|
||||
|
||||
bool
|
||||
RegExpAssertion::IsAnchoredAtStart()
|
||||
{
|
||||
return assertion_type() == RegExpAssertion::START_OF_INPUT;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpAssertion::IsAnchoredAtEnd()
|
||||
{
|
||||
return assertion_type() == RegExpAssertion::END_OF_INPUT;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpCharacterClass
|
||||
|
||||
void
|
||||
RegExpCharacterClass::AppendToText(RegExpText* text)
|
||||
{
|
||||
text->AddElement(TextElement::CharClass(this));
|
||||
}
|
||||
|
||||
CharacterRangeVector &
|
||||
CharacterSet::ranges(LifoAlloc *alloc)
|
||||
{
|
||||
if (ranges_ == nullptr) {
|
||||
ranges_ = alloc->newInfallible<CharacterRangeVector>(*alloc);
|
||||
CharacterRange::AddClassEscape(alloc, standard_set_type_, ranges_);
|
||||
}
|
||||
return *ranges_;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpAtom
|
||||
|
||||
void
|
||||
RegExpAtom::AppendToText(RegExpText* text)
|
||||
{
|
||||
text->AddElement(TextElement::Atom(this));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpText
|
||||
|
||||
void
|
||||
RegExpText::AppendToText(RegExpText* text)
|
||||
{
|
||||
for (size_t i = 0; i < elements().length(); i++)
|
||||
text->AddElement(elements()[i]);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpQuantifier
|
||||
|
||||
Interval
|
||||
RegExpQuantifier::CaptureRegisters()
|
||||
{
|
||||
return body()->CaptureRegisters();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpCapture
|
||||
|
||||
bool
|
||||
RegExpCapture::IsAnchoredAtStart()
|
||||
{
|
||||
return body()->IsAnchoredAtStart();
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpCapture::IsAnchoredAtEnd()
|
||||
{
|
||||
return body()->IsAnchoredAtEnd();
|
||||
}
|
||||
|
||||
Interval
|
||||
RegExpCapture::CaptureRegisters()
|
||||
{
|
||||
Interval self(StartRegister(index()), EndRegister(index()));
|
||||
return self.Union(body()->CaptureRegisters());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpLookahead
|
||||
|
||||
Interval
|
||||
RegExpLookahead::CaptureRegisters()
|
||||
{
|
||||
return body()->CaptureRegisters();
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpLookahead::IsAnchoredAtStart()
|
||||
{
|
||||
return is_positive() && body()->IsAnchoredAtStart();
|
||||
}
|
|
@ -0,0 +1,439 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_REGEXP_AST_H_
|
||||
#define V8_REGEXP_AST_H_
|
||||
|
||||
#include "irregexp/RegExpEngine.h"
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
class RegExpCompiler;
|
||||
class RegExpNode;
|
||||
|
||||
class RegExpVisitor
|
||||
{
|
||||
public:
|
||||
virtual ~RegExpVisitor() { }
|
||||
#define MAKE_CASE(Name) \
|
||||
virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
|
||||
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
|
||||
#undef MAKE_CASE
|
||||
};
|
||||
|
||||
class RegExpTree
|
||||
{
|
||||
public:
|
||||
static const int kInfinity = INT32_MAX;
|
||||
virtual ~RegExpTree() {}
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) = 0;
|
||||
virtual bool IsTextElement() { return false; }
|
||||
virtual bool IsAnchoredAtStart() { return false; }
|
||||
virtual bool IsAnchoredAtEnd() { return false; }
|
||||
virtual int min_match() = 0;
|
||||
virtual int max_match() = 0;
|
||||
// Returns the interval of registers used for captures within this
|
||||
// expression.
|
||||
virtual Interval CaptureRegisters() { return Interval::Empty(); }
|
||||
virtual void AppendToText(RegExpText* text) {
|
||||
MOZ_ASSUME_UNREACHABLE("Bad call");
|
||||
}
|
||||
#define MAKE_ASTYPE(Name) \
|
||||
virtual RegExp##Name* As##Name(); \
|
||||
virtual bool Is##Name();
|
||||
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ASTYPE)
|
||||
#undef MAKE_ASTYPE
|
||||
};
|
||||
|
||||
typedef Vector<RegExpTree *, 1, LifoAllocPolicy<Infallible> > RegExpTreeVector;
|
||||
|
||||
class RegExpDisjunction : public RegExpTree
|
||||
{
|
||||
public:
|
||||
explicit RegExpDisjunction(RegExpTreeVector *alternatives);
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpDisjunction* AsDisjunction();
|
||||
virtual Interval CaptureRegisters();
|
||||
virtual bool IsDisjunction();
|
||||
virtual bool IsAnchoredAtStart();
|
||||
virtual bool IsAnchoredAtEnd();
|
||||
virtual int min_match() { return min_match_; }
|
||||
virtual int max_match() { return max_match_; }
|
||||
|
||||
const RegExpTreeVector &alternatives() { return *alternatives_; }
|
||||
|
||||
private:
|
||||
RegExpTreeVector *alternatives_;
|
||||
int min_match_;
|
||||
int max_match_;
|
||||
};
|
||||
|
||||
class RegExpAlternative : public RegExpTree
|
||||
{
|
||||
public:
|
||||
explicit RegExpAlternative(RegExpTreeVector *nodes);
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpAlternative* AsAlternative();
|
||||
virtual Interval CaptureRegisters();
|
||||
virtual bool IsAlternative();
|
||||
virtual bool IsAnchoredAtStart();
|
||||
virtual bool IsAnchoredAtEnd();
|
||||
virtual int min_match() { return min_match_; }
|
||||
virtual int max_match() { return max_match_; }
|
||||
|
||||
const RegExpTreeVector &nodes() { return *nodes_; }
|
||||
|
||||
private:
|
||||
RegExpTreeVector *nodes_;
|
||||
int min_match_;
|
||||
int max_match_;
|
||||
};
|
||||
|
||||
class RegExpAssertion : public RegExpTree {
|
||||
public:
|
||||
enum AssertionType {
|
||||
START_OF_LINE,
|
||||
START_OF_INPUT,
|
||||
END_OF_LINE,
|
||||
END_OF_INPUT,
|
||||
BOUNDARY,
|
||||
NON_BOUNDARY
|
||||
};
|
||||
explicit RegExpAssertion(AssertionType type) : assertion_type_(type) { }
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpAssertion* AsAssertion();
|
||||
virtual bool IsAssertion();
|
||||
virtual bool IsAnchoredAtStart();
|
||||
virtual bool IsAnchoredAtEnd();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return 0; }
|
||||
AssertionType assertion_type() { return assertion_type_; }
|
||||
private:
|
||||
AssertionType assertion_type_;
|
||||
};
|
||||
|
||||
class CharacterSet
|
||||
{
|
||||
public:
|
||||
explicit CharacterSet(jschar standard_set_type)
|
||||
: ranges_(nullptr),
|
||||
standard_set_type_(standard_set_type)
|
||||
{}
|
||||
explicit CharacterSet(CharacterRangeVector *ranges)
|
||||
: ranges_(ranges),
|
||||
standard_set_type_(0)
|
||||
{}
|
||||
|
||||
CharacterRangeVector &ranges(LifoAlloc *alloc);
|
||||
jschar standard_set_type() { return standard_set_type_; }
|
||||
void set_standard_set_type(jschar special_set_type) {
|
||||
standard_set_type_ = special_set_type;
|
||||
}
|
||||
bool is_standard() { return standard_set_type_ != 0; }
|
||||
void Canonicalize();
|
||||
|
||||
private:
|
||||
CharacterRangeVector *ranges_;
|
||||
|
||||
// If non-zero, the value represents a standard set (e.g., all whitespace
|
||||
// characters) without having to expand the ranges.
|
||||
jschar standard_set_type_;
|
||||
};
|
||||
|
||||
class RegExpCharacterClass : public RegExpTree
|
||||
{
|
||||
public:
|
||||
RegExpCharacterClass(CharacterRangeVector *ranges, bool is_negated)
|
||||
: set_(ranges),
|
||||
is_negated_(is_negated)
|
||||
{}
|
||||
|
||||
explicit RegExpCharacterClass(jschar type)
|
||||
: set_(type),
|
||||
is_negated_(false)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpCharacterClass* AsCharacterClass();
|
||||
virtual bool IsCharacterClass();
|
||||
virtual bool IsTextElement() { return true; }
|
||||
virtual int min_match() { return 1; }
|
||||
virtual int max_match() { return 1; }
|
||||
virtual void AppendToText(RegExpText* text);
|
||||
|
||||
CharacterSet character_set() { return set_; }
|
||||
|
||||
// TODO(lrn): Remove need for complex version if is_standard that
|
||||
// recognizes a mangled standard set and just do { return set_.is_special(); }
|
||||
bool is_standard(LifoAlloc *alloc);
|
||||
|
||||
// Returns a value representing the standard character set if is_standard()
|
||||
// returns true.
|
||||
// Currently used values are:
|
||||
// s : unicode whitespace
|
||||
// S : unicode non-whitespace
|
||||
// w : ASCII word character (digit, letter, underscore)
|
||||
// W : non-ASCII word character
|
||||
// d : ASCII digit
|
||||
// D : non-ASCII digit
|
||||
// . : non-unicode non-newline
|
||||
// * : All characters
|
||||
jschar standard_type() { return set_.standard_set_type(); }
|
||||
|
||||
CharacterRangeVector &ranges(LifoAlloc *alloc) { return set_.ranges(alloc); }
|
||||
bool is_negated() { return is_negated_; }
|
||||
|
||||
private:
|
||||
CharacterSet set_;
|
||||
bool is_negated_;
|
||||
};
|
||||
|
||||
typedef Vector<jschar, 10, LifoAllocPolicy<Infallible> > CharacterVector;
|
||||
|
||||
class RegExpAtom : public RegExpTree
|
||||
{
|
||||
public:
|
||||
explicit RegExpAtom(CharacterVector *data)
|
||||
: data_(data)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpAtom* AsAtom();
|
||||
virtual bool IsAtom();
|
||||
virtual bool IsTextElement() { return true; }
|
||||
virtual int min_match() { return data_->length(); }
|
||||
virtual int max_match() { return data_->length(); }
|
||||
virtual void AppendToText(RegExpText* text);
|
||||
|
||||
const CharacterVector &data() { return *data_; }
|
||||
int length() { return data_->length(); }
|
||||
|
||||
private:
|
||||
CharacterVector *data_;
|
||||
};
|
||||
|
||||
class RegExpText : public RegExpTree
|
||||
{
|
||||
public:
|
||||
explicit RegExpText(LifoAlloc *alloc)
|
||||
: elements_(*alloc), length_(0)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpText* AsText();
|
||||
virtual bool IsText();
|
||||
virtual bool IsTextElement() { return true; }
|
||||
virtual int min_match() { return length_; }
|
||||
virtual int max_match() { return length_; }
|
||||
virtual void AppendToText(RegExpText* text);
|
||||
|
||||
void AddElement(TextElement elm) {
|
||||
elements_.append(elm);
|
||||
length_ += elm.length();
|
||||
}
|
||||
const TextElementVector &elements() { return elements_; }
|
||||
|
||||
private:
|
||||
TextElementVector elements_;
|
||||
int length_;
|
||||
};
|
||||
|
||||
class RegExpQuantifier : public RegExpTree
|
||||
{
|
||||
public:
|
||||
enum QuantifierType { GREEDY, NON_GREEDY, POSSESSIVE };
|
||||
RegExpQuantifier(int min, int max, QuantifierType type, RegExpTree* body)
|
||||
: body_(body),
|
||||
min_(min),
|
||||
max_(max),
|
||||
min_match_(min * body->min_match()),
|
||||
quantifier_type_(type)
|
||||
{
|
||||
if (max > 0 && body->max_match() > kInfinity / max) {
|
||||
max_match_ = kInfinity;
|
||||
} else {
|
||||
max_match_ = max * body->max_match();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
static RegExpNode* ToNode(int min,
|
||||
int max,
|
||||
bool is_greedy,
|
||||
RegExpTree* body,
|
||||
RegExpCompiler* compiler,
|
||||
RegExpNode* on_success,
|
||||
bool not_at_start = false);
|
||||
virtual RegExpQuantifier* AsQuantifier();
|
||||
virtual Interval CaptureRegisters();
|
||||
virtual bool IsQuantifier();
|
||||
virtual int min_match() { return min_match_; }
|
||||
virtual int max_match() { return max_match_; }
|
||||
int min() { return min_; }
|
||||
int max() { return max_; }
|
||||
bool is_possessive() { return quantifier_type_ == POSSESSIVE; }
|
||||
bool is_non_greedy() { return quantifier_type_ == NON_GREEDY; }
|
||||
bool is_greedy() { return quantifier_type_ == GREEDY; }
|
||||
RegExpTree* body() { return body_; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
int min_;
|
||||
int max_;
|
||||
int min_match_;
|
||||
int max_match_;
|
||||
QuantifierType quantifier_type_;
|
||||
};
|
||||
|
||||
class RegExpCapture : public RegExpTree
|
||||
{
|
||||
public:
|
||||
explicit RegExpCapture(RegExpTree* body, int index)
|
||||
: body_(body), index_(index)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
static RegExpNode* ToNode(RegExpTree* body,
|
||||
int index,
|
||||
RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpCapture* AsCapture();
|
||||
virtual bool IsAnchoredAtStart();
|
||||
virtual bool IsAnchoredAtEnd();
|
||||
virtual Interval CaptureRegisters();
|
||||
virtual bool IsCapture();
|
||||
virtual int min_match() { return body_->min_match(); }
|
||||
virtual int max_match() { return body_->max_match(); }
|
||||
RegExpTree* body() { return body_; }
|
||||
int index() { return index_; }
|
||||
static int StartRegister(int index) { return index * 2; }
|
||||
static int EndRegister(int index) { return index * 2 + 1; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
int index_;
|
||||
};
|
||||
|
||||
class RegExpLookahead : public RegExpTree
|
||||
{
|
||||
public:
|
||||
RegExpLookahead(RegExpTree* body,
|
||||
bool is_positive,
|
||||
int capture_count,
|
||||
int capture_from)
|
||||
: body_(body),
|
||||
is_positive_(is_positive),
|
||||
capture_count_(capture_count),
|
||||
capture_from_(capture_from)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpLookahead* AsLookahead();
|
||||
virtual Interval CaptureRegisters();
|
||||
virtual bool IsLookahead();
|
||||
virtual bool IsAnchoredAtStart();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return 0; }
|
||||
RegExpTree* body() { return body_; }
|
||||
bool is_positive() { return is_positive_; }
|
||||
int capture_count() { return capture_count_; }
|
||||
int capture_from() { return capture_from_; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
bool is_positive_;
|
||||
int capture_count_;
|
||||
int capture_from_;
|
||||
};
|
||||
|
||||
typedef Vector<RegExpCapture *, 1, LifoAllocPolicy<Infallible> > RegExpCaptureVector;
|
||||
|
||||
class RegExpBackReference : public RegExpTree
|
||||
{
|
||||
public:
|
||||
explicit RegExpBackReference(RegExpCapture* capture)
|
||||
: capture_(capture)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpBackReference* AsBackReference();
|
||||
virtual bool IsBackReference();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return capture_->max_match(); }
|
||||
int index() { return capture_->index(); }
|
||||
RegExpCapture* capture() { return capture_; }
|
||||
private:
|
||||
RegExpCapture* capture_;
|
||||
};
|
||||
|
||||
class RegExpEmpty : public RegExpTree
|
||||
{
|
||||
public:
|
||||
RegExpEmpty()
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpEmpty* AsEmpty();
|
||||
virtual bool IsEmpty();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return 0; }
|
||||
static RegExpEmpty* GetInstance() {
|
||||
static RegExpEmpty instance;
|
||||
return &instance;
|
||||
}
|
||||
};
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // V8_REGEXP_AST_H_
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_BYTECODES_IRREGEXP_H_
|
||||
#define V8_BYTECODES_IRREGEXP_H_
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
const int BYTECODE_MASK = 0xff;
|
||||
|
||||
// The first argument is packed in with the byte code in one word, so it
|
||||
// has 24 bits, but it can be positive and negative so only use 23 bits for
|
||||
// positive values.
|
||||
const unsigned int MAX_FIRST_ARG = 0x7fffffu;
|
||||
const int BYTECODE_SHIFT = 8;
|
||||
|
||||
#define BYTECODE_ITERATOR(V) \
|
||||
V(BREAK, 0, 4) /* bc8 */ \
|
||||
V(PUSH_CP, 1, 4) /* bc8 pad24 */ \
|
||||
V(PUSH_BT, 2, 8) /* bc8 pad24 offset32 */ \
|
||||
V(PUSH_REGISTER, 3, 4) /* bc8 reg_idx24 */ \
|
||||
V(SET_REGISTER_TO_CP, 4, 8) /* bc8 reg_idx24 offset32 */ \
|
||||
V(SET_CP_TO_REGISTER, 5, 4) /* bc8 reg_idx24 */ \
|
||||
V(SET_REGISTER_TO_SP, 6, 4) /* bc8 reg_idx24 */ \
|
||||
V(SET_SP_TO_REGISTER, 7, 4) /* bc8 reg_idx24 */ \
|
||||
V(SET_REGISTER, 8, 8) /* bc8 reg_idx24 value32 */ \
|
||||
V(ADVANCE_REGISTER, 9, 8) /* bc8 reg_idx24 value32 */ \
|
||||
V(POP_CP, 10, 4) /* bc8 pad24 */ \
|
||||
V(POP_BT, 11, 4) /* bc8 pad24 */ \
|
||||
V(POP_REGISTER, 12, 4) /* bc8 reg_idx24 */ \
|
||||
V(FAIL, 13, 4) /* bc8 pad24 */ \
|
||||
V(SUCCEED, 14, 4) /* bc8 pad24 */ \
|
||||
V(ADVANCE_CP, 15, 4) /* bc8 offset24 */ \
|
||||
V(GOTO, 16, 8) /* bc8 pad24 addr32 */ \
|
||||
V(LOAD_CURRENT_CHAR, 17, 8) /* bc8 offset24 addr32 */ \
|
||||
V(LOAD_CURRENT_CHAR_UNCHECKED, 18, 4) /* bc8 offset24 */ \
|
||||
V(LOAD_2_CURRENT_CHARS, 19, 8) /* bc8 offset24 addr32 */ \
|
||||
V(LOAD_2_CURRENT_CHARS_UNCHECKED, 20, 4) /* bc8 offset24 */ \
|
||||
V(LOAD_4_CURRENT_CHARS, 21, 8) /* bc8 offset24 addr32 */ \
|
||||
V(LOAD_4_CURRENT_CHARS_UNCHECKED, 22, 4) /* bc8 offset24 */ \
|
||||
V(CHECK_4_CHARS, 23, 12) /* bc8 pad24 uint32 addr32 */ \
|
||||
V(CHECK_CHAR, 24, 8) /* bc8 pad8 uint16 addr32 */ \
|
||||
V(CHECK_NOT_4_CHARS, 25, 12) /* bc8 pad24 uint32 addr32 */ \
|
||||
V(CHECK_NOT_CHAR, 26, 8) /* bc8 pad8 uint16 addr32 */ \
|
||||
V(AND_CHECK_4_CHARS, 27, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
|
||||
V(AND_CHECK_CHAR, 28, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
|
||||
V(AND_CHECK_NOT_4_CHARS, 29, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
|
||||
V(AND_CHECK_NOT_CHAR, 30, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
|
||||
V(MINUS_AND_CHECK_NOT_CHAR, 31, 12) /* bc8 pad8 uc16 uc16 uc16 addr32 */ \
|
||||
V(CHECK_CHAR_IN_RANGE, 32, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
|
||||
V(CHECK_CHAR_NOT_IN_RANGE, 33, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
|
||||
V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
|
||||
V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 39, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 40, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_EQ_POS, 42, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \
|
||||
V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \
|
||||
V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */
|
||||
|
||||
#define DECLARE_BYTECODES(name, code, length) \
|
||||
static const int BC_##name = code;
|
||||
BYTECODE_ITERATOR(DECLARE_BYTECODES)
|
||||
#undef DECLARE_BYTECODES
|
||||
|
||||
#define DECLARE_BYTECODE_LENGTH(name, code, length) \
|
||||
static const int BC_##name##_LENGTH = length;
|
||||
BYTECODE_ITERATOR(DECLARE_BYTECODE_LENGTH)
|
||||
#undef DECLARE_BYTECODE_LENGTH
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // V8_BYTECODES_IRREGEXP_H_
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,456 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// A simple interpreter for the Irregexp byte code.
|
||||
|
||||
#include "irregexp/RegExpBytecode.h"
|
||||
#include "irregexp/RegExpMacroAssembler.h"
|
||||
#include "vm/MatchPairs.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::irregexp;
|
||||
|
||||
static const size_t kBitsPerByte = 8;
|
||||
static const size_t kBitsPerByteLog2 = 3;
|
||||
|
||||
class MOZ_STACK_CLASS RegExpStackCursor
|
||||
{
|
||||
public:
|
||||
RegExpStackCursor(JSContext *cx)
|
||||
: cx(cx), cursor(base())
|
||||
{}
|
||||
|
||||
bool push(int32_t value) {
|
||||
*cursor++ = value;
|
||||
if (cursor >= stack().limit()) {
|
||||
int32_t pos = position();
|
||||
if (!stack().grow()) {
|
||||
js_ReportOverRecursed(cx);
|
||||
return false;
|
||||
}
|
||||
setPosition(pos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t pop() {
|
||||
JS_ASSERT(cursor > base());
|
||||
return *--cursor;
|
||||
}
|
||||
|
||||
int32_t peek() {
|
||||
JS_ASSERT(cursor > base());
|
||||
return *(cursor - 1);
|
||||
}
|
||||
|
||||
int32_t position() {
|
||||
return cursor - base();
|
||||
}
|
||||
|
||||
void setPosition(int32_t position) {
|
||||
cursor = base() + position;
|
||||
JS_ASSERT(cursor < stack().limit());
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext *cx;
|
||||
|
||||
int32_t *cursor;
|
||||
|
||||
RegExpStack &stack() { return cx->runtime()->mainThread.regexpStack; }
|
||||
int32_t *base() { return (int32_t *) stack().base(); }
|
||||
};
|
||||
|
||||
static int32_t
|
||||
Load32Aligned(const uint8_t* pc)
|
||||
{
|
||||
JS_ASSERT((reinterpret_cast<uintptr_t>(pc) & 3) == 0);
|
||||
return *reinterpret_cast<const int32_t *>(pc);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
Load16Aligned(const uint8_t* pc)
|
||||
{
|
||||
JS_ASSERT((reinterpret_cast<uintptr_t>(pc) & 1) == 0);
|
||||
return *reinterpret_cast<const uint16_t *>(pc);
|
||||
}
|
||||
|
||||
#define BYTECODE(name) case BC_##name:
|
||||
|
||||
RegExpRunStatus
|
||||
irregexp::InterpretCode(JSContext *cx, const uint8_t *byteCode,
|
||||
const jschar *chars, size_t current, size_t length, MatchPairs *matches)
|
||||
{
|
||||
const uint8_t* pc = byteCode;
|
||||
|
||||
jschar current_char = current ? chars[current - 1] : '\n';
|
||||
|
||||
RegExpStackCursor stack(cx);
|
||||
|
||||
int32_t numRegisters = Load32Aligned(pc);
|
||||
pc += 4;
|
||||
|
||||
Vector<int32_t, 0, SystemAllocPolicy> registers;
|
||||
if (!registers.growByUninitialized(numRegisters))
|
||||
return RegExpRunStatus_Error;
|
||||
for (size_t i = 0; i < matches->length() * 2; i++)
|
||||
registers[i] = -1;
|
||||
|
||||
while (true) {
|
||||
int32_t insn = Load32Aligned(pc);
|
||||
switch (insn & BYTECODE_MASK) {
|
||||
BYTECODE(BREAK)
|
||||
MOZ_ASSUME_UNREACHABLE("Bad bytecode");
|
||||
return RegExpRunStatus_Error;
|
||||
BYTECODE(PUSH_CP)
|
||||
if (!stack.push(current))
|
||||
return RegExpRunStatus_Error;
|
||||
pc += BC_PUSH_CP_LENGTH;
|
||||
break;
|
||||
BYTECODE(PUSH_BT)
|
||||
if (!stack.push(Load32Aligned(pc + 4)))
|
||||
return RegExpRunStatus_Error;
|
||||
pc += BC_PUSH_BT_LENGTH;
|
||||
break;
|
||||
BYTECODE(PUSH_REGISTER)
|
||||
if (!stack.push(registers[insn >> BYTECODE_SHIFT]))
|
||||
return RegExpRunStatus_Error;
|
||||
pc += BC_PUSH_REGISTER_LENGTH;
|
||||
break;
|
||||
BYTECODE(SET_REGISTER)
|
||||
registers[insn >> BYTECODE_SHIFT] = Load32Aligned(pc + 4);
|
||||
pc += BC_SET_REGISTER_LENGTH;
|
||||
break;
|
||||
BYTECODE(ADVANCE_REGISTER)
|
||||
registers[insn >> BYTECODE_SHIFT] += Load32Aligned(pc + 4);
|
||||
pc += BC_ADVANCE_REGISTER_LENGTH;
|
||||
break;
|
||||
BYTECODE(SET_REGISTER_TO_CP)
|
||||
registers[insn >> BYTECODE_SHIFT] = current + Load32Aligned(pc + 4);
|
||||
pc += BC_SET_REGISTER_TO_CP_LENGTH;
|
||||
break;
|
||||
BYTECODE(SET_CP_TO_REGISTER)
|
||||
current = registers[insn >> BYTECODE_SHIFT];
|
||||
pc += BC_SET_CP_TO_REGISTER_LENGTH;
|
||||
break;
|
||||
BYTECODE(SET_REGISTER_TO_SP)
|
||||
registers[insn >> BYTECODE_SHIFT] = stack.position();
|
||||
pc += BC_SET_REGISTER_TO_SP_LENGTH;
|
||||
break;
|
||||
BYTECODE(SET_SP_TO_REGISTER)
|
||||
stack.setPosition(registers[insn >> BYTECODE_SHIFT]);
|
||||
pc += BC_SET_SP_TO_REGISTER_LENGTH;
|
||||
break;
|
||||
BYTECODE(POP_CP)
|
||||
current = stack.pop();
|
||||
pc += BC_POP_CP_LENGTH;
|
||||
break;
|
||||
BYTECODE(POP_BT)
|
||||
pc = byteCode + stack.pop();
|
||||
break;
|
||||
BYTECODE(POP_REGISTER)
|
||||
registers[insn >> BYTECODE_SHIFT] = stack.pop();
|
||||
pc += BC_POP_REGISTER_LENGTH;
|
||||
break;
|
||||
BYTECODE(FAIL)
|
||||
return RegExpRunStatus_Success_NotFound;
|
||||
BYTECODE(SUCCEED)
|
||||
memcpy(matches->pairsRaw(), registers.begin(), matches->length() * 2 * sizeof(int32_t));
|
||||
return RegExpRunStatus_Success;
|
||||
BYTECODE(ADVANCE_CP)
|
||||
current += insn >> BYTECODE_SHIFT;
|
||||
pc += BC_ADVANCE_CP_LENGTH;
|
||||
break;
|
||||
BYTECODE(GOTO)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
BYTECODE(ADVANCE_CP_AND_GOTO)
|
||||
current += insn >> BYTECODE_SHIFT;
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
BYTECODE(CHECK_GREEDY)
|
||||
if ((int32_t)current == stack.peek()) {
|
||||
stack.pop();
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
pc += BC_CHECK_GREEDY_LENGTH;
|
||||
}
|
||||
break;
|
||||
BYTECODE(LOAD_CURRENT_CHAR) {
|
||||
size_t pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos >= length) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
current_char = chars[pos];
|
||||
pc += BC_LOAD_CURRENT_CHAR_LENGTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
BYTECODE(LOAD_CURRENT_CHAR_UNCHECKED) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
current_char = chars[pos];
|
||||
pc += BC_LOAD_CURRENT_CHAR_UNCHECKED_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(LOAD_2_CURRENT_CHARS) {
|
||||
size_t pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos + 2 > length) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
jschar next = chars[pos + 1];
|
||||
current_char = (chars[pos] | (next << (kBitsPerByte * sizeof(jschar))));
|
||||
pc += BC_LOAD_2_CURRENT_CHARS_LENGTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
BYTECODE(LOAD_2_CURRENT_CHARS_UNCHECKED) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
jschar next = chars[pos + 1];
|
||||
current_char = (chars[pos] | (next << (kBitsPerByte * sizeof(jschar))));
|
||||
pc += BC_LOAD_2_CURRENT_CHARS_UNCHECKED_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(LOAD_4_CURRENT_CHARS)
|
||||
MOZ_ASSUME_UNREACHABLE("Ascii handling implemented");
|
||||
break;
|
||||
BYTECODE(LOAD_4_CURRENT_CHARS_UNCHECKED)
|
||||
MOZ_ASSUME_UNREACHABLE("Ascii handling implemented");
|
||||
BYTECODE(CHECK_4_CHARS) {
|
||||
uint32_t c = Load32Aligned(pc + 4);
|
||||
if (c == current_char)
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_CHECK_4_CHARS_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_CHAR) {
|
||||
uint32_t c = (insn >> BYTECODE_SHIFT);
|
||||
if (c == current_char)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_CHAR_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_4_CHARS) {
|
||||
uint32_t c = Load32Aligned(pc + 4);
|
||||
if (c != current_char)
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_CHECK_NOT_4_CHARS_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_CHAR) {
|
||||
uint32_t c = (insn >> BYTECODE_SHIFT);
|
||||
if (c != current_char)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_NOT_CHAR_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(AND_CHECK_4_CHARS) {
|
||||
uint32_t c = Load32Aligned(pc + 4);
|
||||
if (c == (current_char & Load32Aligned(pc + 8)))
|
||||
pc = byteCode + Load32Aligned(pc + 12);
|
||||
else
|
||||
pc += BC_AND_CHECK_4_CHARS_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(AND_CHECK_CHAR) {
|
||||
uint32_t c = (insn >> BYTECODE_SHIFT);
|
||||
if (c == (current_char & Load32Aligned(pc + 4)))
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_AND_CHECK_CHAR_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(AND_CHECK_NOT_4_CHARS) {
|
||||
uint32_t c = Load32Aligned(pc + 4);
|
||||
if (c != (current_char & Load32Aligned(pc + 8)))
|
||||
pc = byteCode + Load32Aligned(pc + 12);
|
||||
else
|
||||
pc += BC_AND_CHECK_NOT_4_CHARS_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(AND_CHECK_NOT_CHAR) {
|
||||
uint32_t c = (insn >> BYTECODE_SHIFT);
|
||||
if (c != (current_char & Load32Aligned(pc + 4)))
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_AND_CHECK_NOT_CHAR_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(MINUS_AND_CHECK_NOT_CHAR) {
|
||||
uint32_t c = (insn >> BYTECODE_SHIFT);
|
||||
uint32_t minus = Load16Aligned(pc + 4);
|
||||
uint32_t mask = Load16Aligned(pc + 6);
|
||||
if (c != ((current_char - minus) & mask))
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_MINUS_AND_CHECK_NOT_CHAR_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_CHAR_IN_RANGE) {
|
||||
uint32_t from = Load16Aligned(pc + 4);
|
||||
uint32_t to = Load16Aligned(pc + 6);
|
||||
if (from <= current_char && current_char <= to)
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_CHECK_CHAR_IN_RANGE_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_CHAR_NOT_IN_RANGE) {
|
||||
uint32_t from = Load16Aligned(pc + 4);
|
||||
uint32_t to = Load16Aligned(pc + 6);
|
||||
if (from > current_char || current_char > to)
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_CHECK_CHAR_NOT_IN_RANGE_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_BIT_IN_TABLE) {
|
||||
int mask = RegExpMacroAssembler::kTableMask;
|
||||
uint8_t b = pc[8 + ((current_char & mask) >> kBitsPerByteLog2)];
|
||||
int bit = (current_char & (kBitsPerByte - 1));
|
||||
if ((b & (1 << bit)) != 0)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_BIT_IN_TABLE_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_LT) {
|
||||
uint32_t limit = (insn >> BYTECODE_SHIFT);
|
||||
if (current_char < limit)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_LT_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_GT) {
|
||||
uint32_t limit = (insn >> BYTECODE_SHIFT);
|
||||
if (current_char > limit)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_GT_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_REGISTER_LT)
|
||||
if (registers[insn >> BYTECODE_SHIFT] < Load32Aligned(pc + 4))
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_CHECK_REGISTER_LT_LENGTH;
|
||||
break;
|
||||
BYTECODE(CHECK_REGISTER_GE)
|
||||
if (registers[insn >> BYTECODE_SHIFT] >= Load32Aligned(pc + 4))
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
else
|
||||
pc += BC_CHECK_REGISTER_GE_LENGTH;
|
||||
break;
|
||||
BYTECODE(CHECK_REGISTER_EQ_POS)
|
||||
if (registers[insn >> BYTECODE_SHIFT] == (int32_t) current)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_REGISTER_EQ_POS_LENGTH;
|
||||
break;
|
||||
BYTECODE(CHECK_NOT_REGS_EQUAL)
|
||||
if (registers[insn >> BYTECODE_SHIFT] == registers[Load32Aligned(pc + 4)])
|
||||
pc += BC_CHECK_NOT_REGS_EQUAL_LENGTH;
|
||||
else
|
||||
pc = byteCode + Load32Aligned(pc + 8);
|
||||
break;
|
||||
BYTECODE(CHECK_NOT_BACK_REF) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (current + len > length) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (chars[from + i] != chars[current + i]) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < len) break;
|
||||
current += len;
|
||||
}
|
||||
pc += BC_CHECK_NOT_BACK_REF_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (current + len > length) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
if (CaseInsensitiveCompareStrings(chars + from, chars + current, len * 2)) {
|
||||
current += len;
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
|
||||
} else {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_AT_START)
|
||||
if (current == 0)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_AT_START_LENGTH;
|
||||
break;
|
||||
BYTECODE(CHECK_NOT_AT_START)
|
||||
if (current == 0)
|
||||
pc += BC_CHECK_NOT_AT_START_LENGTH;
|
||||
else
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
BYTECODE(SET_CURRENT_POSITION_FROM_END) {
|
||||
size_t by = static_cast<uint32_t>(insn) >> BYTECODE_SHIFT;
|
||||
if (length - current > by) {
|
||||
current = length - by;
|
||||
current_char = chars[current - 1];
|
||||
}
|
||||
pc += BC_SET_CURRENT_POSITION_FROM_END_LENGTH;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Bad bytecode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,525 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "irregexp/RegExpMacroAssembler.h"
|
||||
|
||||
#include "irregexp/RegExpBytecode.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::irregexp;
|
||||
|
||||
int
|
||||
irregexp::CaseInsensitiveCompareStrings(const jschar *substring1, const jschar *substring2,
|
||||
size_t byteLength)
|
||||
{
|
||||
JS_ASSERT(byteLength % 2 == 0);
|
||||
size_t length = byteLength >> 1;
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
jschar c1 = substring1[i];
|
||||
jschar c2 = substring2[i];
|
||||
if (c1 != c2) {
|
||||
c1 = unicode::ToLowerCase(c1);
|
||||
c2 = unicode::ToLowerCase(c2);
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
InterpretedRegExpMacroAssembler::InterpretedRegExpMacroAssembler(LifoAlloc *alloc, RegExpShared *shared,
|
||||
size_t numSavedRegisters)
|
||||
: RegExpMacroAssembler(*alloc, shared, numSavedRegisters),
|
||||
pc_(0),
|
||||
advance_current_start_(0),
|
||||
advance_current_offset_(0),
|
||||
advance_current_end_(kInvalidPC),
|
||||
buffer_(nullptr),
|
||||
length_(0)
|
||||
{
|
||||
// The first int32 word is the number of registers.
|
||||
Emit32(0);
|
||||
}
|
||||
|
||||
InterpretedRegExpMacroAssembler::~InterpretedRegExpMacroAssembler()
|
||||
{
|
||||
js_free(buffer_);
|
||||
}
|
||||
|
||||
RegExpCode
|
||||
InterpretedRegExpMacroAssembler::GenerateCode(JSContext *cx)
|
||||
{
|
||||
Bind(&backtrack_);
|
||||
Emit(BC_POP_BT, 0);
|
||||
|
||||
// Update the number of registers.
|
||||
*(int32_t *)buffer_ = num_registers_;
|
||||
|
||||
RegExpCode res;
|
||||
res.byteCode = buffer_;
|
||||
buffer_ = nullptr;
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::AdvanceCurrentPosition(int by)
|
||||
{
|
||||
JS_ASSERT(by >= kMinCPOffset);
|
||||
JS_ASSERT(by <= kMaxCPOffset);
|
||||
advance_current_start_ = pc_;
|
||||
advance_current_offset_ = by;
|
||||
Emit(BC_ADVANCE_CP, by);
|
||||
advance_current_end_ = pc_;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::AdvanceRegister(int reg, int by)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_ADVANCE_REGISTER, reg);
|
||||
Emit32(by);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Backtrack()
|
||||
{
|
||||
Emit(BC_POP_BT, 0);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Bind(jit::Label* label)
|
||||
{
|
||||
advance_current_end_ = kInvalidPC;
|
||||
JS_ASSERT(!label->bound());
|
||||
if (label->used()) {
|
||||
int pos = label->offset();
|
||||
while (pos != jit::Label::INVALID_OFFSET) {
|
||||
int fixup = pos;
|
||||
pos = *reinterpret_cast<int32_t*>(buffer_ + fixup);
|
||||
*reinterpret_cast<uint32_t*>(buffer_ + fixup) = pc_;
|
||||
}
|
||||
}
|
||||
label->bind(pc_);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckAtStart(jit::Label* on_at_start)
|
||||
{
|
||||
Emit(BC_CHECK_AT_START, 0);
|
||||
EmitOrLink(on_at_start);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckCharacter(unsigned c, jit::Label* on_equal)
|
||||
{
|
||||
if (c > MAX_FIRST_ARG) {
|
||||
Emit(BC_CHECK_4_CHARS, 0);
|
||||
Emit32(c);
|
||||
} else {
|
||||
Emit(BC_CHECK_CHAR, c);
|
||||
}
|
||||
EmitOrLink(on_equal);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_equal)
|
||||
{
|
||||
if (c > MAX_FIRST_ARG) {
|
||||
Emit(BC_AND_CHECK_4_CHARS, 0);
|
||||
Emit32(c);
|
||||
} else {
|
||||
Emit(BC_AND_CHECK_CHAR, c);
|
||||
}
|
||||
Emit32(and_with);
|
||||
EmitOrLink(on_equal);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckCharacterGT(jschar limit, jit::Label* on_greater)
|
||||
{
|
||||
Emit(BC_CHECK_GT, limit);
|
||||
EmitOrLink(on_greater);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckCharacterLT(jschar limit, jit::Label* on_less)
|
||||
{
|
||||
Emit(BC_CHECK_LT, limit);
|
||||
EmitOrLink(on_less);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckGreedyLoop(jit::Label* on_tos_equals_current_position)
|
||||
{
|
||||
Emit(BC_CHECK_GREEDY, 0);
|
||||
EmitOrLink(on_tos_equals_current_position);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotAtStart(jit::Label* on_not_at_start)
|
||||
{
|
||||
Emit(BC_CHECK_NOT_AT_START, 0);
|
||||
EmitOrLink(on_not_at_start);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, jit::Label* on_no_match)
|
||||
{
|
||||
JS_ASSERT(start_reg >= 0);
|
||||
JS_ASSERT(start_reg <= kMaxRegister);
|
||||
Emit(BC_CHECK_NOT_BACK_REF, start_reg);
|
||||
EmitOrLink(on_no_match);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match)
|
||||
{
|
||||
JS_ASSERT(start_reg >= 0);
|
||||
JS_ASSERT(start_reg <= kMaxRegister);
|
||||
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
|
||||
EmitOrLink(on_no_match);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotCharacter(unsigned c, jit::Label* on_not_equal)
|
||||
{
|
||||
if (c > MAX_FIRST_ARG) {
|
||||
Emit(BC_CHECK_NOT_4_CHARS, 0);
|
||||
Emit32(c);
|
||||
} else {
|
||||
Emit(BC_CHECK_NOT_CHAR, c);
|
||||
}
|
||||
EmitOrLink(on_not_equal);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotCharacterAfterAnd(unsigned c, unsigned and_with,
|
||||
jit::Label* on_not_equal)
|
||||
{
|
||||
if (c > MAX_FIRST_ARG) {
|
||||
Emit(BC_AND_CHECK_NOT_4_CHARS, 0);
|
||||
Emit32(c);
|
||||
} else {
|
||||
Emit(BC_AND_CHECK_NOT_CHAR, c);
|
||||
}
|
||||
Emit32(and_with);
|
||||
EmitOrLink(on_not_equal);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotCharacterAfterMinusAnd(jschar c, jschar minus, jschar and_with,
|
||||
jit::Label* on_not_equal)
|
||||
{
|
||||
Emit(BC_MINUS_AND_CHECK_NOT_CHAR, c);
|
||||
Emit16(minus);
|
||||
Emit16(and_with);
|
||||
EmitOrLink(on_not_equal);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckCharacterInRange(jschar from, jschar to,
|
||||
jit::Label* on_in_range)
|
||||
{
|
||||
Emit(BC_CHECK_CHAR_IN_RANGE, 0);
|
||||
Emit16(from);
|
||||
Emit16(to);
|
||||
EmitOrLink(on_in_range);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckCharacterNotInRange(jschar from, jschar to,
|
||||
jit::Label* on_not_in_range)
|
||||
{
|
||||
Emit(BC_CHECK_CHAR_NOT_IN_RANGE, 0);
|
||||
Emit16(from);
|
||||
Emit16(to);
|
||||
EmitOrLink(on_not_in_range);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckBitInTable(uint8_t *table, jit::Label* on_bit_set)
|
||||
{
|
||||
static const int kBitsPerByte = 8;
|
||||
|
||||
Emit(BC_CHECK_BIT_IN_TABLE, 0);
|
||||
EmitOrLink(on_bit_set);
|
||||
for (int i = 0; i < kTableSize; i += kBitsPerByte) {
|
||||
int byte = 0;
|
||||
for (int j = 0; j < kBitsPerByte; j++) {
|
||||
if (table[i + j] != 0)
|
||||
byte |= 1 << j;
|
||||
}
|
||||
Emit8(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::JumpOrBacktrack(jit::Label *to)
|
||||
{
|
||||
if (advance_current_end_ == pc_) {
|
||||
// Combine advance current and goto.
|
||||
pc_ = advance_current_start_;
|
||||
Emit(BC_ADVANCE_CP_AND_GOTO, advance_current_offset_);
|
||||
EmitOrLink(to);
|
||||
advance_current_end_ = kInvalidPC;
|
||||
} else {
|
||||
// Regular goto.
|
||||
Emit(BC_GOTO, 0);
|
||||
EmitOrLink(to);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Fail()
|
||||
{
|
||||
Emit(BC_FAIL, 0);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::IfRegisterGE(int reg, int comparand, jit::Label* if_ge)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_CHECK_REGISTER_GE, reg);
|
||||
Emit32(comparand);
|
||||
EmitOrLink(if_ge);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::IfRegisterLT(int reg, int comparand, jit::Label* if_lt)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_CHECK_REGISTER_LT, reg);
|
||||
Emit32(comparand);
|
||||
EmitOrLink(if_lt);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::IfRegisterEqPos(int reg, jit::Label* if_eq)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_CHECK_REGISTER_EQ_POS, reg);
|
||||
EmitOrLink(if_eq);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::LoadCurrentCharacter(int cp_offset, jit::Label* on_end_of_input,
|
||||
bool check_bounds, int characters)
|
||||
{
|
||||
JS_ASSERT(cp_offset >= kMinCPOffset);
|
||||
JS_ASSERT(cp_offset <= kMaxCPOffset);
|
||||
int bytecode;
|
||||
if (check_bounds) {
|
||||
if (characters == 4) {
|
||||
bytecode = BC_LOAD_4_CURRENT_CHARS;
|
||||
} else if (characters == 2) {
|
||||
bytecode = BC_LOAD_2_CURRENT_CHARS;
|
||||
} else {
|
||||
JS_ASSERT(characters == 1);
|
||||
bytecode = BC_LOAD_CURRENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
if (characters == 4) {
|
||||
bytecode = BC_LOAD_4_CURRENT_CHARS_UNCHECKED;
|
||||
} else if (characters == 2) {
|
||||
bytecode = BC_LOAD_2_CURRENT_CHARS_UNCHECKED;
|
||||
} else {
|
||||
JS_ASSERT(characters == 1);
|
||||
bytecode = BC_LOAD_CURRENT_CHAR_UNCHECKED;
|
||||
}
|
||||
}
|
||||
Emit(bytecode, cp_offset);
|
||||
if (check_bounds)
|
||||
EmitOrLink(on_end_of_input);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::PopCurrentPosition()
|
||||
{
|
||||
Emit(BC_POP_CP, 0);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::PopRegister(int reg)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_POP_REGISTER, reg);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::PushCurrentPosition()
|
||||
{
|
||||
Emit(BC_PUSH_CP, 0);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::PushRegister(int reg, StackCheckFlag check_stack_limit)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_PUSH_REGISTER, reg);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::ReadCurrentPositionFromRegister(int reg)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_SET_CP_TO_REGISTER, reg);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::ReadBacktrackStackPointerFromRegister(int reg)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_SET_SP_TO_REGISTER, reg);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::SetCurrentPositionFromEnd(int by)
|
||||
{
|
||||
JS_ASSERT(by >= 0 && by < (1 << 24));
|
||||
Emit(BC_SET_CURRENT_POSITION_FROM_END, by);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::SetRegister(int reg, int to)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_SET_REGISTER, reg);
|
||||
Emit32(to);
|
||||
}
|
||||
|
||||
bool
|
||||
InterpretedRegExpMacroAssembler::Succeed()
|
||||
{
|
||||
Emit(BC_SUCCEED, 0);
|
||||
|
||||
// Restart matching for global regexp not supported.
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::WriteCurrentPositionToRegister(int reg, int cp_offset)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_SET_REGISTER_TO_CP, reg);
|
||||
Emit32(cp_offset); // Current position offset.
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::ClearRegisters(int reg_from, int reg_to)
|
||||
{
|
||||
JS_ASSERT(reg_from <= reg_to);
|
||||
for (int reg = reg_from; reg <= reg_to; reg++)
|
||||
SetRegister(reg, -1);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::WriteBacktrackStackPointerToRegister(int reg)
|
||||
{
|
||||
checkRegister(reg);
|
||||
Emit(BC_SET_REGISTER_TO_SP, reg);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::PushBacktrack(jit::Label *label)
|
||||
{
|
||||
Emit(BC_PUSH_BT, 0);
|
||||
EmitOrLink(label);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::BindBacktrack(jit::Label *label)
|
||||
{
|
||||
Bind(label);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::EmitOrLink(jit::Label *label)
|
||||
{
|
||||
if (label == nullptr)
|
||||
label = &backtrack_;
|
||||
if (label->bound()) {
|
||||
Emit32(label->offset());
|
||||
} else {
|
||||
int pos = label->use(pc_);
|
||||
Emit32(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Emit(uint32_t byte, uint32_t twenty_four_bits)
|
||||
{
|
||||
uint32_t word = ((twenty_four_bits << BYTECODE_SHIFT) | byte);
|
||||
Emit32(word);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Emit32(uint32_t word)
|
||||
{
|
||||
JS_ASSERT(pc_ <= length_);
|
||||
if (pc_ + 3 >= length_)
|
||||
Expand();
|
||||
*reinterpret_cast<uint32_t*>(buffer_ + pc_) = word;
|
||||
pc_ += 4;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Emit16(uint32_t word)
|
||||
{
|
||||
JS_ASSERT(pc_ <= length_);
|
||||
if (pc_ + 1 >= length_)
|
||||
Expand();
|
||||
*reinterpret_cast<uint16_t*>(buffer_ + pc_) = word;
|
||||
pc_ += 2;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Emit8(uint32_t word)
|
||||
{
|
||||
JS_ASSERT(pc_ <= length_);
|
||||
if (pc_ == length_)
|
||||
Expand();
|
||||
*reinterpret_cast<unsigned char*>(buffer_ + pc_) = word;
|
||||
pc_ += 1;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::Expand()
|
||||
{
|
||||
int newLength = Max(100, length_ * 2);
|
||||
if (newLength < length_ + 4)
|
||||
CrashAtUnhandlableOOM("InterpretedRegExpMacroAssembler::Expand");
|
||||
|
||||
buffer_ = (uint8_t *) js_realloc(buffer_, newLength);
|
||||
if (!buffer_)
|
||||
CrashAtUnhandlableOOM("InterpretedRegExpMacroAssembler::Expand");
|
||||
length_ = newLength;
|
||||
}
|
|
@ -0,0 +1,305 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_REGEXP_MACRO_ASSEMBLER_H_
|
||||
#define V8_REGEXP_MACRO_ASSEMBLER_H_
|
||||
|
||||
#include "irregexp/RegExpAST.h"
|
||||
#include "irregexp/RegExpEngine.h"
|
||||
#include "jit/IonMacroAssembler.h"
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
class MOZ_STACK_CLASS RegExpMacroAssembler
|
||||
{
|
||||
public:
|
||||
RegExpMacroAssembler(LifoAlloc &alloc, RegExpShared *shared, size_t numSavedRegisters)
|
||||
: slow_safe_compiler_(false),
|
||||
global_mode_(NOT_GLOBAL),
|
||||
alloc_(alloc),
|
||||
num_registers_(numSavedRegisters),
|
||||
num_saved_registers_(numSavedRegisters),
|
||||
shared(shared)
|
||||
{}
|
||||
|
||||
enum StackCheckFlag {
|
||||
kNoStackLimitCheck = false,
|
||||
kCheckStackLimit = true
|
||||
};
|
||||
|
||||
// The implementation must be able to handle at least:
|
||||
static const int kMaxRegister = (1 << 16) - 1;
|
||||
static const int kMaxCPOffset = (1 << 15) - 1;
|
||||
static const int kMinCPOffset = -(1 << 15);
|
||||
|
||||
static const int kTableSizeBits = 7;
|
||||
static const int kTableSize = 1 << kTableSizeBits;
|
||||
static const int kTableMask = kTableSize - 1;
|
||||
|
||||
// Controls the generation of large inlined constants in the code.
|
||||
void set_slow_safe(bool ssc) { slow_safe_compiler_ = ssc; }
|
||||
bool slow_safe() { return slow_safe_compiler_; }
|
||||
|
||||
enum GlobalMode { NOT_GLOBAL, GLOBAL, GLOBAL_NO_ZERO_LENGTH_CHECK };
|
||||
|
||||
// Set whether the regular expression has the global flag. Exiting due to
|
||||
// a failure in a global regexp may still mean success overall.
|
||||
inline void set_global_mode(GlobalMode mode) { global_mode_ = mode; }
|
||||
inline bool global() { return global_mode_ != NOT_GLOBAL; }
|
||||
inline bool global_with_zero_length_check() {
|
||||
return global_mode_ == GLOBAL;
|
||||
}
|
||||
|
||||
LifoAlloc &alloc() { return alloc_; }
|
||||
|
||||
virtual RegExpCode GenerateCode(JSContext *cx) = 0;
|
||||
|
||||
// The maximal number of pushes between stack checks. Users must supply
|
||||
// kCheckStackLimit flag to push operations (instead of kNoStackLimitCheck)
|
||||
// at least once for every stack_limit() pushes that are executed.
|
||||
virtual int stack_limit_slack() = 0;
|
||||
|
||||
virtual bool CanReadUnaligned() { return false; }
|
||||
|
||||
virtual void AdvanceCurrentPosition(int by) = 0; // Signed cp change.
|
||||
virtual void AdvanceRegister(int reg, int by) = 0; // r[reg] += by.
|
||||
|
||||
// Continues execution from the position pushed on the top of the backtrack
|
||||
// stack by an earlier PushBacktrack.
|
||||
virtual void Backtrack() = 0;
|
||||
|
||||
virtual void Bind(jit::Label* label) = 0;
|
||||
virtual void CheckAtStart(jit::Label* on_at_start) = 0;
|
||||
|
||||
// Dispatch after looking the current character up in a 2-bits-per-entry
|
||||
// map. The destinations vector has up to 4 labels.
|
||||
virtual void CheckCharacter(unsigned c, jit::Label* on_equal) = 0;
|
||||
|
||||
// Bitwise and the current character with the given constant and then
|
||||
// check for a match with c.
|
||||
virtual void CheckCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_equal) = 0;
|
||||
|
||||
virtual void CheckCharacterGT(jschar limit, jit::Label* on_greater) = 0;
|
||||
virtual void CheckCharacterLT(jschar limit, jit::Label* on_less) = 0;
|
||||
virtual void CheckGreedyLoop(jit::Label* on_tos_equals_current_position) = 0;
|
||||
virtual void CheckNotAtStart(jit::Label* on_not_at_start) = 0;
|
||||
virtual void CheckNotBackReference(int start_reg, jit::Label* on_no_match) = 0;
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match) = 0;
|
||||
|
||||
// Check the current character for a match with a literal character. If we
|
||||
// fail to match then goto the on_failure label. End of input always
|
||||
// matches. If the label is nullptr then we should pop a backtrack address off
|
||||
// the stack and go to that.
|
||||
virtual void CheckNotCharacter(unsigned c, jit::Label* on_not_equal) = 0;
|
||||
virtual void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal) = 0;
|
||||
|
||||
// Subtract a constant from the current character, then and with the given
|
||||
// constant and then check for a match with c.
|
||||
virtual void CheckNotCharacterAfterMinusAnd(jschar c,
|
||||
jschar minus,
|
||||
jschar and_with,
|
||||
jit::Label* on_not_equal) = 0;
|
||||
|
||||
virtual void CheckCharacterInRange(jschar from, jschar to, // Both inclusive.
|
||||
jit::Label* on_in_range) = 0;
|
||||
|
||||
virtual void CheckCharacterNotInRange(jschar from, jschar to, // Both inclusive.
|
||||
jit::Label* on_not_in_range) = 0;
|
||||
|
||||
// The current character (modulus the kTableSize) is looked up in the byte
|
||||
// array, and if the found byte is non-zero, we jump to the on_bit_set label.
|
||||
virtual void CheckBitInTable(uint8_t *table, jit::Label* on_bit_set) = 0;
|
||||
|
||||
// Checks whether the given offset from the current position is before
|
||||
// the end of the string. May overwrite the current character.
|
||||
virtual void CheckPosition(int cp_offset, jit::Label* on_outside_input) {
|
||||
LoadCurrentCharacter(cp_offset, on_outside_input, true);
|
||||
}
|
||||
|
||||
// Jump to either the target label or the top of the backtrack stack.
|
||||
virtual void JumpOrBacktrack(jit::Label *to) = 0;
|
||||
|
||||
// Check whether a standard/default character class matches the current
|
||||
// character. Returns false if the type of special character class does
|
||||
// not have custom support.
|
||||
// May clobber the current loaded character.
|
||||
virtual bool CheckSpecialCharacterClass(jschar type, jit::Label* on_no_match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void Fail() = 0;
|
||||
|
||||
// Check whether a register is >= a given constant and go to a label if it
|
||||
// is. Backtracks instead if the label is nullptr.
|
||||
virtual void IfRegisterGE(int reg, int comparand, jit::Label *if_ge) = 0;
|
||||
|
||||
// Check whether a register is < a given constant and go to a label if it is.
|
||||
// Backtracks instead if the label is nullptr.
|
||||
virtual void IfRegisterLT(int reg, int comparand, jit::Label *if_lt) = 0;
|
||||
|
||||
// Check whether a register is == to the current position and go to a
|
||||
// label if it is.
|
||||
virtual void IfRegisterEqPos(int reg, jit::Label *if_eq) = 0;
|
||||
|
||||
virtual void LoadCurrentCharacter(int cp_offset,
|
||||
jit::Label *on_end_of_input,
|
||||
bool check_bounds = true,
|
||||
int characters = 1) = 0;
|
||||
virtual void PopCurrentPosition() = 0;
|
||||
virtual void PopRegister(int register_index) = 0;
|
||||
|
||||
virtual void PushCurrentPosition() = 0;
|
||||
virtual void PushRegister(int register_index, StackCheckFlag check_stack_limit) = 0;
|
||||
virtual void ReadCurrentPositionFromRegister(int reg) = 0;
|
||||
virtual void ReadBacktrackStackPointerFromRegister(int reg) = 0;
|
||||
virtual void SetCurrentPositionFromEnd(int by) = 0;
|
||||
virtual void SetRegister(int register_index, int to) = 0;
|
||||
|
||||
// Return whether the matching (with a global regexp) will be restarted.
|
||||
virtual bool Succeed() = 0;
|
||||
|
||||
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset) = 0;
|
||||
virtual void ClearRegisters(int reg_from, int reg_to) = 0;
|
||||
virtual void WriteBacktrackStackPointerToRegister(int reg) = 0;
|
||||
|
||||
// Pushes the label on the backtrack stack, so that a following Backtrack
|
||||
// will go to this label. Always checks the backtrack stack limit.
|
||||
virtual void PushBacktrack(jit::Label *label) = 0;
|
||||
|
||||
// Bind a label that was previously used by PushBacktrack.
|
||||
virtual void BindBacktrack(jit::Label *label) = 0;
|
||||
|
||||
private:
|
||||
bool slow_safe_compiler_;
|
||||
GlobalMode global_mode_;
|
||||
LifoAlloc &alloc_;
|
||||
|
||||
protected:
|
||||
int num_registers_;
|
||||
int num_saved_registers_;
|
||||
|
||||
void checkRegister(int reg) {
|
||||
JS_ASSERT(reg >= 0);
|
||||
JS_ASSERT(reg <= kMaxRegister);
|
||||
if (num_registers_ <= reg)
|
||||
num_registers_ = reg + 1;
|
||||
}
|
||||
|
||||
public:
|
||||
RegExpShared *shared;
|
||||
};
|
||||
|
||||
int
|
||||
CaseInsensitiveCompareStrings(const jschar *substring1, const jschar *substring2, size_t byteLength);
|
||||
|
||||
class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler : public RegExpMacroAssembler
|
||||
{
|
||||
public:
|
||||
InterpretedRegExpMacroAssembler(LifoAlloc *alloc, RegExpShared *shared, size_t numSavedRegisters);
|
||||
~InterpretedRegExpMacroAssembler();
|
||||
|
||||
// Inherited virtual methods.
|
||||
RegExpCode GenerateCode(JSContext *cx);
|
||||
void AdvanceCurrentPosition(int by);
|
||||
void AdvanceRegister(int reg, int by);
|
||||
void Backtrack();
|
||||
void Bind(jit::Label* label);
|
||||
void CheckAtStart(jit::Label* on_at_start);
|
||||
void CheckCharacter(unsigned c, jit::Label* on_equal);
|
||||
void CheckCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_equal);
|
||||
void CheckCharacterGT(jschar limit, jit::Label* on_greater);
|
||||
void CheckCharacterLT(jschar limit, jit::Label* on_less);
|
||||
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
|
||||
void CheckNotAtStart(jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterMinusAnd(jschar c, jschar minus, jschar and_with,
|
||||
jit::Label* on_not_equal);
|
||||
void CheckCharacterInRange(jschar from, jschar to,
|
||||
jit::Label* on_in_range);
|
||||
void CheckCharacterNotInRange(jschar from, jschar to,
|
||||
jit::Label* on_not_in_range);
|
||||
void CheckBitInTable(uint8_t *table, jit::Label* on_bit_set);
|
||||
void JumpOrBacktrack(jit::Label *to);
|
||||
void Fail();
|
||||
void IfRegisterGE(int reg, int comparand, jit::Label* if_ge);
|
||||
void IfRegisterLT(int reg, int comparand, jit::Label* if_lt);
|
||||
void IfRegisterEqPos(int reg, jit::Label* if_eq);
|
||||
void LoadCurrentCharacter(int cp_offset, jit::Label* on_end_of_input,
|
||||
bool check_bounds = true, int characters = 1);
|
||||
void PopCurrentPosition();
|
||||
void PopRegister(int register_index);
|
||||
void PushCurrentPosition();
|
||||
void PushRegister(int register_index, StackCheckFlag check_stack_limit);
|
||||
void ReadCurrentPositionFromRegister(int reg);
|
||||
void ReadBacktrackStackPointerFromRegister(int reg);
|
||||
void SetCurrentPositionFromEnd(int by);
|
||||
void SetRegister(int register_index, int to);
|
||||
bool Succeed();
|
||||
void WriteCurrentPositionToRegister(int reg, int cp_offset);
|
||||
void ClearRegisters(int reg_from, int reg_to);
|
||||
void WriteBacktrackStackPointerToRegister(int reg);
|
||||
void PushBacktrack(jit::Label *label);
|
||||
void BindBacktrack(jit::Label *label);
|
||||
|
||||
// The byte-code interpreter checks on each push anyway.
|
||||
int stack_limit_slack() { return 1; }
|
||||
|
||||
private:
|
||||
void Expand();
|
||||
|
||||
// Code and bitmap emission.
|
||||
void EmitOrLink(jit::Label* label);
|
||||
void Emit32(uint32_t x);
|
||||
void Emit16(uint32_t x);
|
||||
void Emit8(uint32_t x);
|
||||
void Emit(uint32_t bc, uint32_t arg);
|
||||
|
||||
jit::Label backtrack_;
|
||||
|
||||
// The program counter.
|
||||
int pc_;
|
||||
|
||||
int advance_current_start_;
|
||||
int advance_current_offset_;
|
||||
int advance_current_end_;
|
||||
|
||||
static const int kInvalidPC = -1;
|
||||
|
||||
uint8_t *buffer_;
|
||||
int length_;
|
||||
};
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // V8_REGEXP_MACRO_ASSEMBLER_H_
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,298 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_PARSER_H_
|
||||
#define V8_PARSER_H_
|
||||
|
||||
#include "irregexp/RegExpAST.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace frontend {
|
||||
class TokenStream;
|
||||
}
|
||||
|
||||
namespace irregexp {
|
||||
|
||||
bool
|
||||
ParsePattern(frontend::TokenStream &ts, LifoAlloc &alloc,
|
||||
const jschar *chars, size_t length, bool multiline,
|
||||
RegExpCompileData *data);
|
||||
|
||||
bool
|
||||
ParsePatternSyntax(frontend::TokenStream &ts, LifoAlloc &alloc,
|
||||
const jschar *chars, size_t length);
|
||||
|
||||
// A BufferedVector is an automatically growing list, just like (and backed
|
||||
// by) a Vector, that is optimized for the case of adding and removing
|
||||
// a single element. The last element added is stored outside the backing list,
|
||||
// and if no more than one element is ever added, the ZoneList isn't even
|
||||
// allocated.
|
||||
// Elements must not be nullptr pointers.
|
||||
template <typename T, int initial_size>
|
||||
class BufferedVector
|
||||
{
|
||||
public:
|
||||
typedef Vector<T*, 1, LifoAllocPolicy<Infallible> > VectorType;
|
||||
|
||||
BufferedVector() : list_(nullptr), last_(nullptr) {}
|
||||
|
||||
// Adds element at end of list. This element is buffered and can
|
||||
// be read using last() or removed using RemoveLast until a new Add or until
|
||||
// RemoveLast or GetList has been called.
|
||||
void Add(LifoAlloc *alloc, T* value) {
|
||||
if (last_ != nullptr) {
|
||||
if (list_ == nullptr) {
|
||||
list_ = alloc->newInfallible<VectorType>(*alloc);
|
||||
list_->reserve(initial_size);
|
||||
}
|
||||
list_->append(last_);
|
||||
}
|
||||
last_ = value;
|
||||
}
|
||||
|
||||
T* last() {
|
||||
JS_ASSERT(last_ != nullptr);
|
||||
return last_;
|
||||
}
|
||||
|
||||
T* RemoveLast() {
|
||||
JS_ASSERT(last_ != nullptr);
|
||||
T* result = last_;
|
||||
if ((list_ != nullptr) && (list_->length() > 0))
|
||||
last_ = list_->popCopy();
|
||||
else
|
||||
last_ = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
T* Get(int i) {
|
||||
JS_ASSERT((0 <= i) && (i < length()));
|
||||
if (list_ == nullptr) {
|
||||
JS_ASSERT(0 == i);
|
||||
return last_;
|
||||
} else {
|
||||
if (size_t(i) == list_->length()) {
|
||||
JS_ASSERT(last_ != nullptr);
|
||||
return last_;
|
||||
} else {
|
||||
return (*list_)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
list_ = nullptr;
|
||||
last_ = nullptr;
|
||||
}
|
||||
|
||||
int length() {
|
||||
int length = (list_ == nullptr) ? 0 : list_->length();
|
||||
return length + ((last_ == nullptr) ? 0 : 1);
|
||||
}
|
||||
|
||||
VectorType *GetList(LifoAlloc *alloc) {
|
||||
if (list_ == nullptr)
|
||||
list_ = alloc->newInfallible<VectorType>(*alloc);
|
||||
if (last_ != nullptr) {
|
||||
list_->append(last_);
|
||||
last_ = nullptr;
|
||||
}
|
||||
return list_;
|
||||
}
|
||||
|
||||
private:
|
||||
VectorType *list_;
|
||||
T* last_;
|
||||
};
|
||||
|
||||
|
||||
// Accumulates RegExp atoms and assertions into lists of terms and alternatives.
|
||||
class RegExpBuilder
|
||||
{
|
||||
public:
|
||||
explicit RegExpBuilder(LifoAlloc *alloc);
|
||||
void AddCharacter(jschar character);
|
||||
// "Adds" an empty expression. Does nothing except consume a
|
||||
// following quantifier
|
||||
void AddEmpty();
|
||||
void AddAtom(RegExpTree* tree);
|
||||
void AddAssertion(RegExpTree* tree);
|
||||
void NewAlternative(); // '|'
|
||||
void AddQuantifierToAtom(int min, int max, RegExpQuantifier::QuantifierType type);
|
||||
RegExpTree* ToRegExp();
|
||||
|
||||
private:
|
||||
void FlushCharacters();
|
||||
void FlushText();
|
||||
void FlushTerms();
|
||||
|
||||
LifoAlloc *alloc;
|
||||
bool pending_empty_;
|
||||
CharacterVector *characters_;
|
||||
BufferedVector<RegExpTree, 2> terms_;
|
||||
BufferedVector<RegExpTree, 2> text_;
|
||||
BufferedVector<RegExpTree, 2> alternatives_;
|
||||
|
||||
enum LastAdded {
|
||||
ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM
|
||||
};
|
||||
mozilla::DebugOnly<LastAdded> last_added_;
|
||||
};
|
||||
|
||||
// Characters parsed by RegExpParser can be either jschars or kEndMarker.
|
||||
typedef uint32_t widechar;
|
||||
|
||||
class RegExpParser
|
||||
{
|
||||
public:
|
||||
RegExpParser(frontend::TokenStream &ts, LifoAlloc *alloc,
|
||||
const jschar *chars, const jschar *end, bool multiline_mode);
|
||||
|
||||
RegExpTree* ParsePattern();
|
||||
RegExpTree* ParseDisjunction();
|
||||
RegExpTree* ParseGroup();
|
||||
RegExpTree* ParseCharacterClass();
|
||||
|
||||
// Parses a {...,...} quantifier and stores the range in the given
|
||||
// out parameters.
|
||||
bool ParseIntervalQuantifier(int* min_out, int* max_out);
|
||||
|
||||
// Parses and returns a single escaped character. The character
|
||||
// must not be 'b' or 'B' since they are usually handled specially.
|
||||
widechar ParseClassCharacterEscape();
|
||||
|
||||
// Checks whether the following is a length-digit hexadecimal number,
|
||||
// and sets the value if it is.
|
||||
bool ParseHexEscape(int length, size_t* value);
|
||||
|
||||
size_t ParseOctalLiteral();
|
||||
|
||||
// Tries to parse the input as a back reference. If successful it
|
||||
// stores the result in the output parameter and returns true. If
|
||||
// it fails it will push back the characters read so the same characters
|
||||
// can be reparsed.
|
||||
bool ParseBackReferenceIndex(int* index_out);
|
||||
|
||||
bool ParseClassAtom(jschar* char_class, CharacterRange *char_range);
|
||||
RegExpTree* ReportError(unsigned errorNumber);
|
||||
void Advance();
|
||||
void Advance(int dist) {
|
||||
next_pos_ += dist - 1;
|
||||
Advance();
|
||||
}
|
||||
|
||||
void Reset(const jschar *pos) {
|
||||
next_pos_ = pos;
|
||||
has_more_ = (pos < end_);
|
||||
Advance();
|
||||
}
|
||||
|
||||
// Reports whether the pattern might be used as a literal search string.
|
||||
// Only use if the result of the parse is a single atom node.
|
||||
bool simple() { return simple_; }
|
||||
bool contains_anchor() { return contains_anchor_; }
|
||||
void set_contains_anchor() { contains_anchor_ = true; }
|
||||
int captures_started() { return captures_ == nullptr ? 0 : captures_->length(); }
|
||||
const jschar *position() { return next_pos_ - 1; }
|
||||
|
||||
static const int kMaxCaptures = 1 << 16;
|
||||
static const widechar kEndMarker = (1 << 21);
|
||||
|
||||
private:
|
||||
enum SubexpressionType {
|
||||
INITIAL,
|
||||
CAPTURE, // All positive values represent captures.
|
||||
POSITIVE_LOOKAHEAD,
|
||||
NEGATIVE_LOOKAHEAD,
|
||||
GROUPING
|
||||
};
|
||||
|
||||
class RegExpParserState {
|
||||
public:
|
||||
RegExpParserState(LifoAlloc *alloc,
|
||||
RegExpParserState* previous_state,
|
||||
SubexpressionType group_type,
|
||||
int disjunction_capture_index)
|
||||
: previous_state_(previous_state),
|
||||
builder_(alloc->newInfallible<RegExpBuilder>(alloc)),
|
||||
group_type_(group_type),
|
||||
disjunction_capture_index_(disjunction_capture_index)
|
||||
{}
|
||||
// Parser state of containing expression, if any.
|
||||
RegExpParserState* previous_state() { return previous_state_; }
|
||||
bool IsSubexpression() { return previous_state_ != nullptr; }
|
||||
// RegExpBuilder building this regexp's AST.
|
||||
RegExpBuilder* builder() { return builder_; }
|
||||
// Type of regexp being parsed (parenthesized group or entire regexp).
|
||||
SubexpressionType group_type() { return group_type_; }
|
||||
// Index in captures array of first capture in this sub-expression, if any.
|
||||
// Also the capture index of this sub-expression itself, if group_type
|
||||
// is CAPTURE.
|
||||
int capture_index() { return disjunction_capture_index_; }
|
||||
|
||||
private:
|
||||
// Linked list implementation of stack of states.
|
||||
RegExpParserState* previous_state_;
|
||||
// Builder for the stored disjunction.
|
||||
RegExpBuilder* builder_;
|
||||
// Stored disjunction type (capture, look-ahead or grouping), if any.
|
||||
SubexpressionType group_type_;
|
||||
// Stored disjunction's capture index (if any).
|
||||
int disjunction_capture_index_;
|
||||
};
|
||||
|
||||
widechar current() { return current_; }
|
||||
bool has_more() { return has_more_; }
|
||||
bool has_next() { return next_pos_ < end_; }
|
||||
widechar Next() {
|
||||
if (has_next())
|
||||
return *next_pos_;
|
||||
return kEndMarker;
|
||||
}
|
||||
void ScanForCaptures();
|
||||
|
||||
frontend::TokenStream &ts;
|
||||
LifoAlloc *alloc;
|
||||
RegExpCaptureVector *captures_;
|
||||
const jschar *next_pos_, *end_;
|
||||
widechar current_;
|
||||
// The capture count is only valid after we have scanned for captures.
|
||||
int capture_count_;
|
||||
bool has_more_;
|
||||
bool multiline_;
|
||||
bool simple_;
|
||||
bool contains_anchor_;
|
||||
bool is_scanned_for_captures_;
|
||||
};
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // V8_PARSER_H_
|
|
@ -0,0 +1,102 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "irregexp/RegExpStack.h"
|
||||
|
||||
#include "vm/Runtime.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::irregexp;
|
||||
|
||||
RegExpStackScope::RegExpStackScope(JSRuntime *rt)
|
||||
: regexp_stack(&rt->mainThread.regexpStack)
|
||||
{}
|
||||
|
||||
RegExpStackScope::~RegExpStackScope()
|
||||
{
|
||||
regexp_stack->reset();
|
||||
}
|
||||
|
||||
int
|
||||
irregexp::GrowBacktrackStack(JSRuntime *rt)
|
||||
{
|
||||
return rt->mainThread.regexpStack.grow();
|
||||
}
|
||||
|
||||
RegExpStack::RegExpStack()
|
||||
: base_(nullptr), size(0), limit_(nullptr)
|
||||
{}
|
||||
|
||||
RegExpStack::~RegExpStack()
|
||||
{
|
||||
js_free(base_);
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpStack::init()
|
||||
{
|
||||
base_ = js_malloc(kMinimumStackSize);
|
||||
if (!base_)
|
||||
return false;
|
||||
|
||||
size = kMinimumStackSize;
|
||||
updateLimit();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RegExpStack::reset()
|
||||
{
|
||||
JS_ASSERT(size >= kMinimumStackSize);
|
||||
|
||||
if (size != kMinimumStackSize) {
|
||||
base_ = js_realloc(base_, kMinimumStackSize);
|
||||
size = kMinimumStackSize;
|
||||
updateLimit();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpStack::grow()
|
||||
{
|
||||
size_t newSize = size * 2;
|
||||
if (newSize > kMaximumStackSize)
|
||||
return false;
|
||||
|
||||
void *newBase = js_realloc(base_, newSize);
|
||||
if (!newBase)
|
||||
return false;
|
||||
|
||||
base_ = newBase;
|
||||
size = newSize;
|
||||
updateLimit();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
|
||||
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_REGEXP_STACK_H_
|
||||
#define V8_REGEXP_STACK_H_
|
||||
|
||||
#include "jspubtd.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
class RegExpStack;
|
||||
|
||||
// Maintains a per-thread stack area that can be used by irregexp
|
||||
// implementation for its backtracking stack.
|
||||
//
|
||||
// Since there is only one stack area, the Irregexp implementation is not
|
||||
// re-entrant. I.e., no regular expressions may be executed in the same thread
|
||||
// during a preempted Irregexp execution.
|
||||
class RegExpStackScope
|
||||
{
|
||||
public:
|
||||
// Create and delete an instance to control the life-time of a growing stack.
|
||||
|
||||
// Initializes the stack memory area if necessary.
|
||||
explicit RegExpStackScope(JSRuntime *rt);
|
||||
|
||||
// Releases the stack if it has grown.
|
||||
~RegExpStackScope();
|
||||
|
||||
private:
|
||||
RegExpStack* regexp_stack;
|
||||
};
|
||||
|
||||
class RegExpStack
|
||||
{
|
||||
public:
|
||||
// Number of allocated locations on the stack above the limit.
|
||||
// No sequence of pushes must be longer that this without doing a stack-limit
|
||||
// check.
|
||||
static const int kStackLimitSlack = 32;
|
||||
|
||||
RegExpStack();
|
||||
~RegExpStack();
|
||||
bool init();
|
||||
|
||||
// Resets the buffer if it has grown beyond the default/minimum size.
|
||||
void reset();
|
||||
|
||||
// Attempts to grow the stack by at least kStackLimitSlack entries.
|
||||
bool grow();
|
||||
|
||||
// Address of allocated memory.
|
||||
const void *addressOfBase() { return &base_; }
|
||||
const void *addressOfLimit() { return &limit_; }
|
||||
|
||||
void *base() { return base_; }
|
||||
void *limit() { return limit_; }
|
||||
|
||||
private:
|
||||
// Artificial limit used when no memory has been allocated.
|
||||
static const uintptr_t kMemoryTop = static_cast<uintptr_t>(-1);
|
||||
|
||||
// Minimal size of allocated stack area, in bytes.
|
||||
static const size_t kMinimumStackSize = 1 * 1024;
|
||||
|
||||
// Maximal size of allocated stack area, in bytes.
|
||||
static const size_t kMaximumStackSize = 64 * 1024 * 1024;
|
||||
|
||||
// If size > 0 then base must be non-nullptr.
|
||||
void *base_;
|
||||
|
||||
// Length in bytes of base.
|
||||
size_t size;
|
||||
|
||||
// If the stack pointer gets above the limit, we should react and
|
||||
// either grow the stack or report an out-of-stack exception.
|
||||
// There is only a limited number of locations above the stack limit,
|
||||
// so users of the stack should check the stack limit during any
|
||||
// sequence of pushes longer than this.
|
||||
void *limit_;
|
||||
|
||||
void updateLimit() {
|
||||
JS_ASSERT(size >= kStackLimitSlack * sizeof(void *));
|
||||
limit_ = static_cast<uint8_t *>(base()) + size - (kStackLimitSlack * sizeof(void *));
|
||||
}
|
||||
};
|
||||
|
||||
int
|
||||
GrowBacktrackStack(JSRuntime *rt);
|
||||
|
||||
}} // namespace js::irregexp
|
||||
|
||||
#endif // V8_REGEXP_STACK_H_
|
|
@ -2,10 +2,6 @@
|
|||
* Check that builtin character classes within ranges produce syntax
|
||||
* errors.
|
||||
*
|
||||
* Note that, per the extension in bug 351463, SpiderMonkey permits hyphens
|
||||
* adjacent to character class escapes in character classes, treating them as a
|
||||
* hyphen pattern character. Therefore /[\d-\s]/ is okay
|
||||
*
|
||||
* Note: /\b/ is the backspace escape, which is a single pattern character,
|
||||
* though it looks deceptively like a character class.
|
||||
*/
|
||||
|
@ -20,9 +16,9 @@ function isRegExpSyntaxError(pattern) {
|
|||
return false;
|
||||
}
|
||||
|
||||
assertEq(isRegExpSyntaxError('[C-\\s]'), true);
|
||||
assertEq(isRegExpSyntaxError('[C-\\d]'), true);
|
||||
assertEq(isRegExpSyntaxError('[C-\\W]'), true);
|
||||
//assertEq(isRegExpSyntaxError('[C-\\s]'), false);
|
||||
//assertEq(isRegExpSyntaxError('[C-\\d]'), false);
|
||||
//assertEq(isRegExpSyntaxError('[C-\\W]'), false);
|
||||
assertEq(isRegExpSyntaxError('[C-]'), false);
|
||||
assertEq(isRegExpSyntaxError('[-C]'), false);
|
||||
assertEq(isRegExpSyntaxError('[C-C]'), false);
|
||||
|
@ -31,8 +27,8 @@ assertEq(isRegExpSyntaxError('[\\b-\\b]'), false);
|
|||
assertEq(isRegExpSyntaxError('[\\B-\\B]'), false);
|
||||
assertEq(isRegExpSyntaxError('[\\b-\\B]'), false);
|
||||
assertEq(isRegExpSyntaxError('[\\B-\\b]'), true);
|
||||
assertEq(isRegExpSyntaxError('[\\b-\\w]'), true);
|
||||
assertEq(isRegExpSyntaxError('[\\B-\\w]'), true);
|
||||
//assertEq(isRegExpSyntaxError('[\\b-\\w]'), false);
|
||||
//assertEq(isRegExpSyntaxError('[\\B-\\w]'), false);
|
||||
|
||||
/* Extension. */
|
||||
assertEq(isRegExpSyntaxError('[\\s-\\s]'), false);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
try {
|
||||
|
||||
function f() {}
|
||||
f("".match(/(?:(?=g)).{2147483648,}/ + (/[]/)), null);
|
||||
|
||||
} catch (e) {} // Yarr throws on the above regexp
|
||||
|
||||
var re = new RegExp("a[\x01-\\xDE]+M", "gi");
|
||||
re.exec("");
|
||||
|
||||
var re = new RegExp("a[\x01-\\u00b8]+?c", "gi");
|
||||
re.exec("");
|
||||
|
||||
var re = new RegExp("a[\x01-\\u00f8]?c", "gi");
|
||||
re.exec("");
|
|
@ -1,9 +0,0 @@
|
|||
// See bug 953013
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function test() {
|
||||
var input = Array(2499999+1).join('a');
|
||||
var result = /^([\w])+$/.test(input);
|
||||
}
|
||||
assertThrowsInstanceOf(test, InternalError);
|
|
@ -3,8 +3,9 @@ function testSlowNativeBail() {
|
|||
try {
|
||||
for (var i = 0; i < a.length; i++)
|
||||
new RegExp(a[i]);
|
||||
assertEq(true, false);
|
||||
} catch (exc) {
|
||||
assertEq(""+exc, "SyntaxError: invalid quantifier");
|
||||
assertEq(exc instanceof SyntaxError, true);
|
||||
}
|
||||
}
|
||||
testSlowNativeBail();
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// |jit-test| error: ReferenceError
|
||||
(function(x) {
|
||||
x = i ? 4 : 2
|
||||
y
|
||||
})()
|
|
@ -50,6 +50,25 @@ function rbitor_object(i) {
|
|||
return i;
|
||||
}
|
||||
|
||||
var uceFault_bitxor_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitxor_number'));
|
||||
function rbitxor_number(i) {
|
||||
var x = 1 ^ i;
|
||||
if (uceFault_bitxor_number(i) || uceFault_bitxor_number(i))
|
||||
assertEq(x, 98 /* = 1 XOR 99 */);
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_bitxor_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitxor_object'));
|
||||
function rbitxor_object(i) {
|
||||
var t = i;
|
||||
var o = { valueOf: function () { return t; } };
|
||||
var x = 1 ^ o; /* computed with t == i, not 1000 */
|
||||
t = 1000;
|
||||
if (uceFault_bitxor_object(i) || uceFault_bitxor_object(i))
|
||||
assertEq(x, 98 /* = 1 XOR 99 */);
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_add_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_add_number'));
|
||||
function radd_number(i) {
|
||||
var x = 1 + i;
|
||||
|
|
|
@ -662,7 +662,7 @@ class ABIArgIter
|
|||
typedef js::Vector<MIRType, 8> MIRTypeVector;
|
||||
typedef ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
|
||||
|
||||
typedef js::Vector<VarType, 8, LifoAllocPolicy> VarTypeVector;
|
||||
typedef js::Vector<VarType, 8, LifoAllocPolicy<Fallible> > VarTypeVector;
|
||||
typedef ABIArgIter<VarTypeVector> ABIArgTypeIter;
|
||||
|
||||
class Signature
|
||||
|
|
|
@ -404,13 +404,31 @@ class CompileInfo
|
|||
return executionMode_ == ParallelExecution;
|
||||
}
|
||||
|
||||
bool canOptimizeOutSlot(uint32_t i) const {
|
||||
if (script()->strict())
|
||||
// Returns true if a slot can be observed out-side the current frame while
|
||||
// the frame is active on the stack. This implies that these definitions
|
||||
// would have to be executed and that they cannot be removed even if they
|
||||
// are unused.
|
||||
bool isObservableSlot(uint32_t slot) const {
|
||||
if (!funMaybeLazy())
|
||||
return false;
|
||||
|
||||
// The |this| value must always be observable.
|
||||
if (slot == thisSlot())
|
||||
return true;
|
||||
|
||||
// Function.arguments can be used to access all arguments in
|
||||
// non-strict scripts, so we can't optimize out any arguments.
|
||||
return !(firstArgSlot() <= i && i - firstArgSlot() < nargs());
|
||||
// If the function may need an arguments object, then make sure to
|
||||
// preserve the scope chain, because it may be needed to construct the
|
||||
// arguments object during bailout. If we've already created an
|
||||
// arguments object (or got one via OSR), preserve that as well.
|
||||
if (hasArguments() && (slot == scopeChainSlot() || slot == argsObjSlot()))
|
||||
return true;
|
||||
|
||||
// Function.arguments can be used to access all arguments in non-strict
|
||||
// scripts, so we can't optimize out any arguments.
|
||||
if (!script()->strict() && firstArgSlot() <= slot && slot - firstArgSlot() < nargs())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -547,8 +547,12 @@ jit::FinishOffThreadBuilder(IonBuilder *builder)
|
|||
builder->script()->ionScript()->clearRecompiling();
|
||||
|
||||
// Clean up if compilation did not succeed.
|
||||
if (CompilingOffThread(builder->script(), executionMode))
|
||||
SetIonScript(builder->script(), executionMode, nullptr);
|
||||
if (CompilingOffThread(builder->script(), executionMode)) {
|
||||
SetIonScript(builder->script(), executionMode,
|
||||
builder->abortReason() == AbortReason_Disable
|
||||
? ION_DISABLED_SCRIPT
|
||||
: nullptr);
|
||||
}
|
||||
|
||||
// The builder is allocated into its LifoAlloc, so destroying that will
|
||||
// destroy the builder and all other data accumulated during compilation,
|
||||
|
|
|
@ -32,12 +32,9 @@ class TempAllocator
|
|||
rootList_(nullptr)
|
||||
{ }
|
||||
|
||||
void *allocateOrCrash(size_t bytes)
|
||||
void *allocateInfallible(size_t bytes)
|
||||
{
|
||||
void *p = lifoScope_.alloc().alloc(bytes);
|
||||
if (!p)
|
||||
js::CrashAtUnhandlableOOM("LifoAlloc::allocOrCrash");
|
||||
return p;
|
||||
return lifoScope_.alloc().allocInfallible(bytes);
|
||||
}
|
||||
|
||||
void *allocate(size_t bytes)
|
||||
|
@ -178,7 +175,7 @@ class AutoIonContextAlloc
|
|||
struct TempObject
|
||||
{
|
||||
inline void *operator new(size_t nbytes, TempAllocator &alloc) {
|
||||
return alloc.allocateOrCrash(nbytes);
|
||||
return alloc.allocateInfallible(nbytes);
|
||||
}
|
||||
template <class T>
|
||||
inline void *operator new(size_t nbytes, T *pos) {
|
||||
|
|
|
@ -107,6 +107,12 @@ jit::EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph)
|
|||
if (ins->isImplicitlyUsed())
|
||||
continue;
|
||||
|
||||
// If the instruction's is captured by one of the resume point, then
|
||||
// it might be observed indirectly while the frame is live on the
|
||||
// stack, so it has to be computed.
|
||||
if (ins->isObserved())
|
||||
continue;
|
||||
|
||||
// Check if this instruction's result is only used within the
|
||||
// current block, and keep track of its last use in a definition
|
||||
// (not resume point). This requires the instructions in the block
|
||||
|
@ -143,14 +149,6 @@ jit::EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph)
|
|||
continue;
|
||||
}
|
||||
|
||||
// The operand is an uneliminable slot. This currently
|
||||
// includes argument slots in non-strict scripts (due to being
|
||||
// observable via Function.arguments).
|
||||
if (!block->info().canOptimizeOutSlot(uses->index())) {
|
||||
uses++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store an optimized out magic value in place of all dead
|
||||
// resume point operands. Making any such substitution can in
|
||||
// general alter the interpreter's behavior, even though the
|
||||
|
@ -232,30 +230,7 @@ IsPhiObservable(MPhi *phi, Observability observe)
|
|||
break;
|
||||
}
|
||||
|
||||
uint32_t slot = phi->slot();
|
||||
CompileInfo &info = phi->block()->info();
|
||||
JSFunction *fun = info.funMaybeLazy();
|
||||
|
||||
// If the Phi is of the |this| value, it must always be observable.
|
||||
if (fun && slot == info.thisSlot())
|
||||
return true;
|
||||
|
||||
// If the function may need an arguments object, then make sure to
|
||||
// preserve the scope chain, because it may be needed to construct the
|
||||
// arguments object during bailout. If we've already created an arguments
|
||||
// object (or got one via OSR), preserve that as well.
|
||||
if (fun && info.hasArguments() &&
|
||||
(slot == info.scopeChainSlot() || slot == info.argsObjSlot()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// The Phi is an uneliminable slot. Currently this includes argument slots
|
||||
// in non-strict scripts (due to being observable via Function.arguments).
|
||||
if (fun && !info.canOptimizeOutSlot(slot))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return phi->isObserved();
|
||||
}
|
||||
|
||||
// Handles cases like:
|
||||
|
|
|
@ -116,7 +116,6 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
|
|||
backgroundCodegen_(nullptr),
|
||||
analysisContext(analysisContext),
|
||||
baselineFrame_(baselineFrame),
|
||||
abortReason_(AbortReason_Disable),
|
||||
descrSetHash_(nullptr),
|
||||
constraints_(constraints),
|
||||
analysis_(*temp, info->script()),
|
||||
|
@ -145,6 +144,7 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
|
|||
{
|
||||
script_ = info->script();
|
||||
pc = info->startPC();
|
||||
abortReason_ = AbortReason_Disable;
|
||||
|
||||
JS_ASSERT(script()->hasBaselineScript() == (info->executionMode() != ArgumentsUsageAnalysis));
|
||||
JS_ASSERT(!!analysisContext == (info->executionMode() == DefinitePropertiesAnalysis));
|
||||
|
@ -444,14 +444,21 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecod
|
|||
for (size_t i = 0; i < loopHeaders_.length(); i++) {
|
||||
if (loopHeaders_[i].pc == start) {
|
||||
MBasicBlock *oldEntry = loopHeaders_[i].header;
|
||||
for (MPhiIterator oldPhi = oldEntry->phisBegin();
|
||||
oldPhi != oldEntry->phisEnd();
|
||||
oldPhi++)
|
||||
{
|
||||
MPhi *newPhi = entry->getSlot(oldPhi->slot())->toPhi();
|
||||
MResumePoint *oldEntryRp = oldEntry->entryResumePoint();
|
||||
size_t stackDepth = oldEntryRp->numOperands();
|
||||
for (size_t slot = 0; slot < stackDepth; slot++) {
|
||||
MDefinition *oldDef = oldEntryRp->getOperand(slot);
|
||||
if (!oldDef->isPhi()) {
|
||||
MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
|
||||
MOZ_ASSERT(oldDef == entry->getSlot(slot));
|
||||
continue;
|
||||
}
|
||||
MPhi *oldPhi = oldDef->toPhi();
|
||||
MPhi *newPhi = entry->getSlot(slot)->toPhi();
|
||||
if (!newPhi->addBackedgeType(oldPhi->type(), oldPhi->resultTypeSet()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the most recent header for this loop encountered, in case
|
||||
// new types flow to the phis and the loop is processed at least
|
||||
// three times.
|
||||
|
@ -1150,26 +1157,24 @@ IonBuilder::maybeAddOsrTypeBarriers()
|
|||
static const size_t OSR_PHI_POSITION = 1;
|
||||
JS_ASSERT(preheader->getPredecessor(OSR_PHI_POSITION) == osrBlock);
|
||||
|
||||
MPhiIterator headerPhi = header->phisBegin();
|
||||
while (headerPhi != header->phisEnd() && headerPhi->slot() < info().startArgSlot())
|
||||
headerPhi++;
|
||||
|
||||
for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++, headerPhi++) {
|
||||
MResumePoint *headerRp = header->entryResumePoint();
|
||||
size_t stackDepth = headerRp->numOperands();
|
||||
MOZ_ASSERT(stackDepth == osrBlock->stackDepth());
|
||||
for (uint32_t slot = info().startArgSlot(); slot < stackDepth; slot++) {
|
||||
// Aliased slots are never accessed, since they need to go through
|
||||
// the callobject. The typebarriers are added there and can be
|
||||
// discarded here.
|
||||
if (info().isSlotAliasedAtOsr(i))
|
||||
if (info().isSlotAliasedAtOsr(slot))
|
||||
continue;
|
||||
|
||||
MInstruction *def = osrBlock->getSlot(i)->toInstruction();
|
||||
|
||||
JS_ASSERT(headerPhi->slot() == i);
|
||||
MPhi *preheaderPhi = preheader->getSlot(i)->toPhi();
|
||||
MInstruction *def = osrBlock->getSlot(slot)->toInstruction();
|
||||
MPhi *preheaderPhi = preheader->getSlot(slot)->toPhi();
|
||||
MPhi *headerPhi = headerRp->getOperand(slot)->toPhi();
|
||||
|
||||
MIRType type = headerPhi->type();
|
||||
types::TemporaryTypeSet *typeSet = headerPhi->resultTypeSet();
|
||||
|
||||
if (!addOsrValueTypeBarrier(i, &def, type, typeSet))
|
||||
if (!addOsrValueTypeBarrier(slot, &def, type, typeSet))
|
||||
return false;
|
||||
|
||||
preheaderPhi->replaceOperand(OSR_PHI_POSITION, def);
|
||||
|
@ -4104,7 +4109,7 @@ IonBuilder::patchInlinedReturns(CallInfo &callInfo, MIRGraphReturns &returns, MB
|
|||
return patchInlinedReturn(callInfo, returns[0], bottom);
|
||||
|
||||
// Accumulate multiple returns with a phi.
|
||||
MPhi *phi = MPhi::New(alloc(), bottom->stackDepth());
|
||||
MPhi *phi = MPhi::New(alloc());
|
||||
if (!phi->reserveLength(returns.length()))
|
||||
return nullptr;
|
||||
|
||||
|
@ -4533,7 +4538,7 @@ IonBuilder::inlineCalls(CallInfo &callInfo, ObjectVector &targets,
|
|||
returnBlock->inheritSlots(dispatchBlock);
|
||||
callInfo.popFormals(returnBlock);
|
||||
|
||||
MPhi *retPhi = MPhi::New(alloc(), returnBlock->stackDepth());
|
||||
MPhi *retPhi = MPhi::New(alloc());
|
||||
returnBlock->addPhi(retPhi);
|
||||
returnBlock->push(retPhi);
|
||||
|
||||
|
|
|
@ -818,8 +818,6 @@ class IonBuilder : public MIRGenerator
|
|||
CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
|
||||
void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
|
||||
|
||||
AbortReason abortReason() { return abortReason_; }
|
||||
|
||||
TypeDescrSetHash *getOrCreateDescrSetHash(); // fallible
|
||||
|
||||
types::CompilerConstraintList *constraints() {
|
||||
|
@ -837,7 +835,6 @@ class IonBuilder : public MIRGenerator
|
|||
|
||||
JSContext *analysisContext;
|
||||
BaselineFrameInspector *baselineFrame_;
|
||||
AbortReason abortReason_;
|
||||
TypeDescrSetHash *descrSetHash_;
|
||||
|
||||
// Constraints for recording dependencies on type information.
|
||||
|
|
|
@ -30,9 +30,6 @@ class Linker
|
|||
|
||||
template <AllowGC allowGC>
|
||||
JitCode *newCode(JSContext *cx, JSC::ExecutableAllocator *execAlloc, JSC::CodeKind kind) {
|
||||
JS_ASSERT(kind == JSC::ION_CODE ||
|
||||
kind == JSC::BASELINE_CODE ||
|
||||
kind == JSC::OTHER_CODE);
|
||||
JS_ASSERT(masm.numAsmJSAbsoluteLinks() == 0);
|
||||
|
||||
gc::AutoSuppressGC suppressGC(cx);
|
||||
|
|
|
@ -1204,14 +1204,6 @@ MacroAssembler::handleFailure(ExecutionMode executionMode)
|
|||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static inline bool
|
||||
IsCompilingAsmJS()
|
||||
{
|
||||
// asm.js compilation pushes an IonContext with a null JSCompartment.
|
||||
IonContext *ictx = MaybeGetIonContext();
|
||||
return ictx && ictx->compartment == nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
AssumeUnreachable_(const char *output) {
|
||||
MOZ_ReportAssertionFailure(output, __FILE__, __LINE__);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef jit_JitCommon_h
|
||||
#define jit_JitCommon_h
|
||||
|
||||
// Various macros used by all JITs, including YARR.
|
||||
// Various macros used by all JITs.
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR)
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
|
@ -21,12 +21,21 @@
|
|||
(js::jit::Simulator::Current()->call( \
|
||||
JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 8, p0, p1, p2, p3, p4, p5, p6, p7) & 0xffffffff)
|
||||
|
||||
#ifdef JS_YARR
|
||||
|
||||
#define CALL_GENERATED_YARR_CODE3(entry, p0, p1, p2) \
|
||||
js::jit::Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 3, p0, p1, p2)
|
||||
|
||||
#define CALL_GENERATED_YARR_CODE4(entry, p0, p1, p2, p3) \
|
||||
js::jit::Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 4, p0, p1, p2, p3)
|
||||
|
||||
#else // JS_YARR
|
||||
|
||||
#define CALL_GENERATED_REGEXP(entry, p0) \
|
||||
js::jit::Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 1, p0)
|
||||
|
||||
#endif // JS_YARR
|
||||
|
||||
#define CALL_GENERATED_ASMJS(entry, p0, p1) \
|
||||
(Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 2, p0, p1) & 0xffffffff)
|
||||
|
||||
|
@ -36,12 +45,21 @@
|
|||
#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7) \
|
||||
entry(p0, p1, p2, p3, p4, p5, p6, p7)
|
||||
|
||||
#ifdef JS_YARR
|
||||
|
||||
#define CALL_GENERATED_YARR_CODE3(entry, p0, p1, p2) \
|
||||
entry(p0, p1, p2)
|
||||
|
||||
#define CALL_GENERATED_YARR_CODE4(entry, p0, p1, p2, p3) \
|
||||
entry(p0, p1, p2, p3)
|
||||
|
||||
#else // JS_YARR
|
||||
|
||||
#define CALL_GENERATED_REGEXP(entry, p0) \
|
||||
entry(p0)
|
||||
|
||||
#endif // JS_YARR
|
||||
|
||||
#define CALL_GENERATED_ASMJS(entry, p0, p1) \
|
||||
entry(p0, p1)
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_Label_h
|
||||
#define jit_Label_h
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
struct LabelBase
|
||||
{
|
||||
protected:
|
||||
// offset_ >= 0 means that the label is either bound or has incoming
|
||||
// uses and needs to be bound.
|
||||
int32_t offset_ : 31;
|
||||
bool bound_ : 1;
|
||||
|
||||
// Disallow assignment.
|
||||
void operator =(const LabelBase &label);
|
||||
public:
|
||||
static const int32_t INVALID_OFFSET = -1;
|
||||
|
||||
LabelBase() : offset_(INVALID_OFFSET), bound_(false)
|
||||
{ }
|
||||
LabelBase(const LabelBase &label)
|
||||
: offset_(label.offset_),
|
||||
bound_(label.bound_)
|
||||
{ }
|
||||
|
||||
// If the label is bound, all incoming edges have been patched and any
|
||||
// future incoming edges will be immediately patched.
|
||||
bool bound() const {
|
||||
return bound_;
|
||||
}
|
||||
int32_t offset() const {
|
||||
JS_ASSERT(bound() || used());
|
||||
return offset_;
|
||||
}
|
||||
// Returns whether the label is not bound, but has incoming uses.
|
||||
bool used() const {
|
||||
return !bound() && offset_ > INVALID_OFFSET;
|
||||
}
|
||||
// Binds the label, fixing its final position in the code stream.
|
||||
void bind(int32_t offset) {
|
||||
JS_ASSERT(!bound());
|
||||
offset_ = offset;
|
||||
bound_ = true;
|
||||
JS_ASSERT(offset_ == offset);
|
||||
}
|
||||
// Marks the label as neither bound nor used.
|
||||
void reset() {
|
||||
offset_ = INVALID_OFFSET;
|
||||
bound_ = false;
|
||||
}
|
||||
// Sets the label's latest used position, returning the old use position in
|
||||
// the process.
|
||||
int32_t use(int32_t offset) {
|
||||
JS_ASSERT(!bound());
|
||||
|
||||
int32_t old = offset_;
|
||||
offset_ = offset;
|
||||
JS_ASSERT(offset_ == offset);
|
||||
|
||||
return old;
|
||||
}
|
||||
};
|
||||
|
||||
// A label represents a position in an assembly buffer that may or may not have
|
||||
// already been generated. Labels can either be "bound" or "unbound", the
|
||||
// former meaning that its position is known and the latter that its position
|
||||
// is not yet known.
|
||||
//
|
||||
// A jump to an unbound label adds that jump to the label's incoming queue. A
|
||||
// jump to a bound label automatically computes the jump distance. The process
|
||||
// of binding a label automatically corrects all incoming jumps.
|
||||
class Label : public LabelBase
|
||||
{
|
||||
public:
|
||||
Label()
|
||||
{ }
|
||||
Label(const Label &label) : LabelBase(label)
|
||||
{ }
|
||||
};
|
||||
|
||||
// Label's destructor asserts that if it has been used it has also been bound.
|
||||
// In the case long-lived labels, however, failed compilation (e.g. OOM) will
|
||||
// trigger this failure innocuously. This Label silences the assertion.
|
||||
class NonAssertingLabel : public Label
|
||||
{
|
||||
};
|
||||
|
||||
} } // namespace js::jit
|
||||
|
||||
#endif // jit_Label_h
|
|
@ -2342,7 +2342,8 @@ MResumePoint::inherit(MBasicBlock *block)
|
|||
}
|
||||
}
|
||||
|
||||
void MResumePoint::dump(FILE *fp) const
|
||||
void
|
||||
MResumePoint::dump(FILE *fp) const
|
||||
{
|
||||
fprintf(fp, "resumepoint mode=");
|
||||
|
||||
|
@ -2377,6 +2378,12 @@ MResumePoint::dump() const
|
|||
dump(stderr);
|
||||
}
|
||||
|
||||
bool
|
||||
MResumePoint::isObservableOperand(size_t index) const
|
||||
{
|
||||
return block()->info().isObservableSlot(index);
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
MToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
|
||||
{
|
||||
|
|
|
@ -67,6 +67,7 @@ MIRType MIRTypeFromValue(const js::Value &vp)
|
|||
_(Movable) /* Allow LICM and GVN to move this instruction */ \
|
||||
_(Lowered) /* (Debug only) has a virtual register */ \
|
||||
_(Guard) /* Not removable if uses == 0 */ \
|
||||
_(Observed) /* Cannot be optimized out */ \
|
||||
\
|
||||
/* Keep the flagged instruction in resume points and do not substitute this
|
||||
* instruction by an UndefinedValue. This might be used by call inlining
|
||||
|
@ -3535,6 +3536,11 @@ class MBitXor : public MBinaryBitwiseInstruction
|
|||
return this;
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
bool writeRecoverData(CompactBufferWriter &writer) const;
|
||||
bool canRecoverOnBailout() const {
|
||||
return specialization_ < MIRType_Object;
|
||||
}
|
||||
};
|
||||
|
||||
class MShiftInstruction
|
||||
|
@ -4687,7 +4693,6 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
|||
{
|
||||
js::Vector<MUse, 2, IonAllocPolicy> inputs_;
|
||||
|
||||
uint32_t slot_;
|
||||
bool hasBackedgeType_;
|
||||
bool triedToSpecialize_;
|
||||
bool isIterator_;
|
||||
|
@ -4707,9 +4712,8 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
|||
public:
|
||||
INSTRUCTION_HEADER(Phi)
|
||||
|
||||
MPhi(TempAllocator &alloc, uint32_t slot, MIRType resultType)
|
||||
MPhi(TempAllocator &alloc, MIRType resultType)
|
||||
: inputs_(alloc),
|
||||
slot_(slot),
|
||||
hasBackedgeType_(false),
|
||||
triedToSpecialize_(false),
|
||||
isIterator_(false),
|
||||
|
@ -4723,8 +4727,8 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
|||
setResultType(resultType);
|
||||
}
|
||||
|
||||
static MPhi *New(TempAllocator &alloc, uint32_t slot, MIRType resultType = MIRType_Value) {
|
||||
return new(alloc) MPhi(alloc, slot, resultType);
|
||||
static MPhi *New(TempAllocator &alloc, MIRType resultType = MIRType_Value) {
|
||||
return new(alloc) MPhi(alloc, resultType);
|
||||
}
|
||||
|
||||
void setOperand(size_t index, MDefinition *operand) {
|
||||
|
@ -4745,9 +4749,6 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
|||
size_t numOperands() const {
|
||||
return inputs_.length();
|
||||
}
|
||||
uint32_t slot() const {
|
||||
return slot_;
|
||||
}
|
||||
bool hasBackedgeType() const {
|
||||
return hasBackedgeType_;
|
||||
}
|
||||
|
@ -9692,8 +9693,12 @@ class MResumePoint MOZ_FINAL : public MNode, public InlineForwardListNode<MResum
|
|||
// Overwrites an operand without updating its Uses.
|
||||
void setOperand(size_t index, MDefinition *operand) {
|
||||
JS_ASSERT(index < stackDepth_);
|
||||
// Note: We do not remove the isObserved flag, as this would imply that
|
||||
// we check the list of uses of the removed MDefinition.
|
||||
operands_[index].set(operand, this, index);
|
||||
operand->addUse(&operands_[index]);
|
||||
if (!operand->isObserved() && isObservableOperand(index))
|
||||
operand->setObserved();
|
||||
}
|
||||
|
||||
void clearOperand(size_t index) {
|
||||
|
@ -9715,8 +9720,12 @@ class MResumePoint MOZ_FINAL : public MNode, public InlineForwardListNode<MResum
|
|||
size_t numOperands() const {
|
||||
return stackDepth_;
|
||||
}
|
||||
|
||||
bool isObservableOperand(size_t index) const;
|
||||
|
||||
MDefinition *getOperand(size_t index) const {
|
||||
JS_ASSERT(index < stackDepth_);
|
||||
MOZ_ASSERT_IF(isObservableOperand(index), operands_[index].producer()->isObserved());
|
||||
return operands_[index].producer();
|
||||
}
|
||||
jsbytecode *pc() const {
|
||||
|
|
|
@ -87,6 +87,13 @@ class MIRGenerator
|
|||
cancelBuild_ = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
abortReason_ = AbortReason_Disable;
|
||||
}
|
||||
AbortReason abortReason() {
|
||||
return abortReason_;
|
||||
}
|
||||
|
||||
bool compilingAsmJS() const {
|
||||
return info_->compilingAsmJS();
|
||||
}
|
||||
|
@ -141,6 +148,7 @@ class MIRGenerator
|
|||
JSFunction *fun_;
|
||||
uint32_t nslots_;
|
||||
MIRGraph *graph_;
|
||||
AbortReason abortReason_;
|
||||
bool error_;
|
||||
mozilla::Atomic<bool, mozilla::Relaxed> cancelBuild_;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ MIRGenerator::MIRGenerator(CompileCompartment *compartment, const JitCompileOpti
|
|||
optimizationInfo_(optimizationInfo),
|
||||
alloc_(alloc),
|
||||
graph_(graph),
|
||||
abortReason_(AbortReason_NoAbort),
|
||||
error_(false),
|
||||
cancelBuild_(false),
|
||||
maxAsmJSStackArgBytes_(0),
|
||||
|
@ -272,15 +273,17 @@ MBasicBlock::NewAsmJS(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred, Kin
|
|||
if (!phis)
|
||||
return nullptr;
|
||||
|
||||
// Note: Phis are inserted in the same order as the slots.
|
||||
for (size_t i = 0; i < nphis; i++) {
|
||||
MDefinition *predSlot = pred->getSlot(i);
|
||||
|
||||
JS_ASSERT(predSlot->type() != MIRType_Value);
|
||||
MPhi *phi = new(phis + i) MPhi(alloc, i, predSlot->type());
|
||||
MPhi *phi = new(phis + i) MPhi(alloc, predSlot->type());
|
||||
|
||||
JS_ALWAYS_TRUE(phi->reserveLength(2));
|
||||
phi->addInput(predSlot);
|
||||
|
||||
// Add append Phis in the block.
|
||||
block->addPhi(phi);
|
||||
block->setSlot(i, phi);
|
||||
}
|
||||
|
@ -389,7 +392,7 @@ MBasicBlock::inherit(TempAllocator &alloc, BytecodeAnalysis *analysis, MBasicBlo
|
|||
if (kind_ == PENDING_LOOP_HEADER) {
|
||||
size_t i = 0;
|
||||
for (i = 0; i < info().firstStackSlot(); i++) {
|
||||
MPhi *phi = MPhi::New(alloc, i);
|
||||
MPhi *phi = MPhi::New(alloc);
|
||||
if (!phi->addInputSlow(pred->getSlot(i)))
|
||||
return false;
|
||||
addPhi(phi);
|
||||
|
@ -410,7 +413,7 @@ MBasicBlock::inherit(TempAllocator &alloc, BytecodeAnalysis *analysis, MBasicBlo
|
|||
}
|
||||
|
||||
for (; i < stackDepth(); i++) {
|
||||
MPhi *phi = MPhi::New(alloc, i);
|
||||
MPhi *phi = MPhi::New(alloc);
|
||||
if (!phi->addInputSlow(pred->getSlot(i)))
|
||||
return false;
|
||||
addPhi(phi);
|
||||
|
@ -909,9 +912,9 @@ MBasicBlock::addPredecessorPopN(TempAllocator &alloc, MBasicBlock *pred, uint32_
|
|||
// Otherwise, create a new phi node.
|
||||
MPhi *phi;
|
||||
if (mine->type() == other->type())
|
||||
phi = MPhi::New(alloc, i, mine->type());
|
||||
phi = MPhi::New(alloc, mine->type());
|
||||
else
|
||||
phi = MPhi::New(alloc, i);
|
||||
phi = MPhi::New(alloc);
|
||||
addPhi(phi);
|
||||
|
||||
// Prime the phi for each predecessor, so input(x) comes from
|
||||
|
@ -991,34 +994,8 @@ MBasicBlock::setBackedge(MBasicBlock *pred)
|
|||
bool hadTypeChange = false;
|
||||
|
||||
// Add exit definitions to each corresponding phi at the entry.
|
||||
for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) {
|
||||
MPhi *entryDef = *phi;
|
||||
MDefinition *exitDef = pred->slots_[entryDef->slot()];
|
||||
|
||||
// Assert that we already placed phis for each slot.
|
||||
JS_ASSERT(entryDef->block() == this);
|
||||
|
||||
if (entryDef == exitDef) {
|
||||
// If the exit def is the same as the entry def, make a redundant
|
||||
// phi. Since loop headers have exactly two incoming edges, we
|
||||
// know that that's just the first input.
|
||||
//
|
||||
// Note that we eliminate later rather than now, to avoid any
|
||||
// weirdness around pending continue edges which might still hold
|
||||
// onto phis.
|
||||
exitDef = entryDef->getOperand(0);
|
||||
}
|
||||
|
||||
bool typeChange = false;
|
||||
|
||||
if (!entryDef->addInputSlow(exitDef, &typeChange))
|
||||
return AbortReason_Alloc;
|
||||
|
||||
hadTypeChange |= typeChange;
|
||||
|
||||
JS_ASSERT(entryDef->slot() < pred->stackDepth());
|
||||
setSlot(entryDef->slot(), entryDef);
|
||||
}
|
||||
if (!inheritPhisFromBackedge(pred, &hadTypeChange))
|
||||
return AbortReason_Alloc;
|
||||
|
||||
if (hadTypeChange) {
|
||||
for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++)
|
||||
|
@ -1047,9 +1024,12 @@ MBasicBlock::setBackedgeAsmJS(MBasicBlock *pred)
|
|||
JS_ASSERT(kind_ == PENDING_LOOP_HEADER);
|
||||
|
||||
// Add exit definitions to each corresponding phi at the entry.
|
||||
for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) {
|
||||
// Note: Phis are inserted in the same order as the slots. (see
|
||||
// MBasicBlock::NewAsmJS)
|
||||
size_t slot = 0;
|
||||
for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++, slot++) {
|
||||
MPhi *entryDef = *phi;
|
||||
MDefinition *exitDef = pred->getSlot(entryDef->slot());
|
||||
MDefinition *exitDef = pred->getSlot(slot);
|
||||
|
||||
// Assert that we already placed phis for each slot.
|
||||
JS_ASSERT(entryDef->block() == this);
|
||||
|
@ -1072,8 +1052,8 @@ MBasicBlock::setBackedgeAsmJS(MBasicBlock *pred)
|
|||
// MBasicBlock::NewAsmJS calls reserveLength(2) for loop header phis.
|
||||
entryDef->addInput(exitDef);
|
||||
|
||||
JS_ASSERT(entryDef->slot() < pred->stackDepth());
|
||||
setSlot(entryDef->slot(), entryDef);
|
||||
MOZ_ASSERT(slot < pred->stackDepth());
|
||||
setSlot(slot, entryDef);
|
||||
}
|
||||
|
||||
// We are now a loop header proper
|
||||
|
@ -1186,13 +1166,23 @@ MBasicBlock::removePredecessor(MBasicBlock *pred)
|
|||
void
|
||||
MBasicBlock::inheritPhis(MBasicBlock *header)
|
||||
{
|
||||
for (MPhiIterator iter = header->phisBegin(); iter != header->phisEnd(); iter++) {
|
||||
MPhi *phi = *iter;
|
||||
JS_ASSERT(phi->numOperands() == 2);
|
||||
MResumePoint *headerRp = header->entryResumePoint();
|
||||
size_t stackDepth = headerRp->numOperands();
|
||||
for (size_t slot = 0; slot < stackDepth; slot++) {
|
||||
MDefinition *exitDef = getSlot(slot);
|
||||
MDefinition *loopDef = headerRp->getOperand(slot);
|
||||
if (!loopDef->isPhi()) {
|
||||
MOZ_ASSERT(loopDef->block()->id() < header->id());
|
||||
MOZ_ASSERT(loopDef == exitDef);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Phis are allocated by NewPendingLoopHeader.
|
||||
MPhi *phi = loopDef->toPhi();
|
||||
MOZ_ASSERT(phi->numOperands() == 2);
|
||||
|
||||
// The entry definition is always the leftmost input to the phi.
|
||||
MDefinition *entryDef = phi->getOperand(0);
|
||||
MDefinition *exitDef = getSlot(phi->slot());
|
||||
|
||||
if (entryDef != exitDef)
|
||||
continue;
|
||||
|
@ -1200,10 +1190,60 @@ MBasicBlock::inheritPhis(MBasicBlock *header)
|
|||
// If the entryDef is the same as exitDef, then we must propagate the
|
||||
// phi down to this successor. This chance was missed as part of
|
||||
// setBackedge() because exits are not captured in resume points.
|
||||
setSlot(phi->slot(), phi);
|
||||
setSlot(slot, phi);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MBasicBlock::inheritPhisFromBackedge(MBasicBlock *backedge, bool *hadTypeChange)
|
||||
{
|
||||
// We must be a pending loop header
|
||||
MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
|
||||
|
||||
size_t stackDepth = entryResumePoint()->numOperands();
|
||||
for (size_t slot = 0; slot < stackDepth; slot++) {
|
||||
// Get the value stack-slot of the back edge.
|
||||
MDefinition *exitDef = backedge->getSlot(slot);
|
||||
|
||||
// Get the value of the loop header.
|
||||
MDefinition *loopDef = entryResumePoint()->getOperand(slot);
|
||||
if (!loopDef->isPhi()) {
|
||||
// If we are finishing a pending loop header, then we need to ensure
|
||||
// that all operands are phis. This is usualy the case, except for
|
||||
// object/arrays build with generators, in which case we share the
|
||||
// same allocations across all blocks.
|
||||
MOZ_ASSERT(loopDef->block()->id() < id());
|
||||
MOZ_ASSERT(loopDef == exitDef);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Phis are allocated by NewPendingLoopHeader.
|
||||
MPhi *entryDef = loopDef->toPhi();
|
||||
MOZ_ASSERT(entryDef->block() == this);
|
||||
|
||||
if (entryDef == exitDef) {
|
||||
// If the exit def is the same as the entry def, make a redundant
|
||||
// phi. Since loop headers have exactly two incoming edges, we
|
||||
// know that that's just the first input.
|
||||
//
|
||||
// Note that we eliminate later rather than now, to avoid any
|
||||
// weirdness around pending continue edges which might still hold
|
||||
// onto phis.
|
||||
exitDef = entryDef->getOperand(0);
|
||||
}
|
||||
|
||||
bool typeChange = false;
|
||||
|
||||
if (!entryDef->addInputSlow(exitDef, &typeChange))
|
||||
return false;
|
||||
|
||||
*hadTypeChange |= typeChange;
|
||||
setSlot(slot, entryDef);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MBasicBlock::specializePhis()
|
||||
{
|
||||
|
|
|
@ -207,6 +207,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||
// Propagates phis placed in a loop header down to this successor block.
|
||||
void inheritPhis(MBasicBlock *header);
|
||||
|
||||
// Propagates backedge slots into phis operands of the loop header.
|
||||
bool inheritPhisFromBackedge(MBasicBlock *backedge, bool *hadTypeChange);
|
||||
|
||||
// Compute the types for phis in this block according to their inputs.
|
||||
bool specializePhis();
|
||||
|
||||
|
|
|
@ -384,6 +384,7 @@ ParallelSafetyAnalysis::analyze()
|
|||
// always bailout.
|
||||
if (*block == graph_.entryBlock()) {
|
||||
Spew(SpewCompile, "Entry block contains unsafe MIR");
|
||||
mir_->disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,32 @@ RBitNot::recover(JSContext *cx, SnapshotIterator &iter) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MBitXor::writeRecoverData(CompactBufferWriter &writer) const
|
||||
{
|
||||
MOZ_ASSERT(canRecoverOnBailout());
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_BitXor));
|
||||
return true;
|
||||
}
|
||||
|
||||
RBitXor::RBitXor(CompactBufferReader &reader)
|
||||
{ }
|
||||
|
||||
bool
|
||||
RBitXor::recover(JSContext *cx, SnapshotIterator &iter) const
|
||||
{
|
||||
RootedValue lhs(cx, iter.read());
|
||||
RootedValue rhs(cx, iter.read());
|
||||
|
||||
int32_t result;
|
||||
if (!js::BitXor(cx, lhs, rhs, &result))
|
||||
return false;
|
||||
|
||||
RootedValue rootedResult(cx, js::Int32Value(result));
|
||||
iter.storeInstructionResult(rootedResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MNewObject::writeRecoverData(CompactBufferWriter &writer) const
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace jit {
|
|||
_(ResumePoint) \
|
||||
_(BitNot) \
|
||||
_(BitOr) \
|
||||
_(BitXor) \
|
||||
_(Add) \
|
||||
_(NewObject) \
|
||||
_(NewDerivedTypedObject)
|
||||
|
@ -102,6 +103,18 @@ class RBitNot MOZ_FINAL : public RInstruction
|
|||
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||
};
|
||||
|
||||
class RBitXor MOZ_FINAL : public RInstruction
|
||||
{
|
||||
public:
|
||||
RINSTRUCTION_HEADER_(BitXor)
|
||||
|
||||
virtual uint32_t numOperands() const {
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||
};
|
||||
|
||||
class RAdd MOZ_FINAL : public RInstruction
|
||||
{
|
||||
private:
|
||||
|
|
|
@ -1924,6 +1924,12 @@ MacroAssemblerARMCompat::sub32(Register src, Register dest)
|
|||
ma_sub(src, dest, SetCond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::and32(Register src, Register dest)
|
||||
{
|
||||
ma_and(src, dest, SetCond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::and32(Imm32 imm, Register dest)
|
||||
{
|
||||
|
@ -2406,6 +2412,12 @@ MacroAssemblerARMCompat::storePtr(Register src, const Address &address)
|
|||
ma_str(src, Operand(address));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::storePtr(Register src, const BaseIndex &address)
|
||||
{
|
||||
store32(src, address);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest)
|
||||
{
|
||||
|
|
|
@ -1295,6 +1295,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
}
|
||||
void xor32(Imm32 imm, Register dest);
|
||||
|
||||
void and32(Register src, Register dest);
|
||||
void and32(Imm32 imm, Register dest);
|
||||
void and32(Imm32 imm, const Address &dest);
|
||||
void or32(Imm32 imm, const Address &dest);
|
||||
|
@ -1370,6 +1371,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
void storePtr(ImmPtr imm, const Address &address);
|
||||
void storePtr(ImmGCPtr imm, const Address &address);
|
||||
void storePtr(Register src, const Address &address);
|
||||
void storePtr(Register src, const BaseIndex &address);
|
||||
void storePtr(Register src, AbsoluteAddress dest);
|
||||
void storeDouble(FloatRegister src, Address addr) {
|
||||
ma_vstr(src, Operand(addr));
|
||||
|
@ -1452,6 +1454,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
void rshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_lsr(imm, dest, dest);
|
||||
}
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
ma_asr(imm, dest, dest);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_lsl(imm, dest, dest);
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче