Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2014-05-16 14:33:31 +02:00
commit ecf9c41329
168 changed files with 14451 additions and 1300 deletions

View File

@ -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);

View File

@ -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"/>

View File

@ -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 -->

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "7a8ed4b21466d68e078529a3737bc5677ba3692f",
"revision": "4393a8d719e7917f543cd8d906632c9b2164198e",
"repo_path": "/integration/gaia-central"
}

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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 -->

View File

@ -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"/>

View File

@ -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;
},

View File

@ -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();

View File

@ -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()

View File

@ -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,

View File

@ -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();

Binary file not shown.

View File

@ -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();
};

View File

@ -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

View File

@ -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>

View File

@ -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)
{

View File

@ -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();

View File

@ -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;
}

View File

@ -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*

View File

@ -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.

View File

@ -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 {

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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;
}
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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);
};
}
}
}

View File

@ -37,7 +37,7 @@
using namespace std;
using mozilla::dom::WebAudioUtils;
using namespace mozilla::dom; // for WebAudioUtils
using mozilla::IsInfinite;
using mozilla::IsNaN;

View File

@ -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;

View File

@ -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)
{

View File

@ -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

View File

@ -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>

View File

@ -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;
}

View File

@ -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>

View File

@ -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();

View File

@ -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();

View File

@ -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();
});

View File

@ -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);
});

View File

@ -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;
};

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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 };

View File

@ -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
};

View File

@ -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 ========================================================

View File

@ -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;
}

View File

@ -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();

View File

@ -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,

View File

@ -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 *

File diff suppressed because it is too large Load Diff

View File

@ -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_

View File

@ -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();
}

439
js/src/irregexp/RegExpAST.h Normal file
View File

@ -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_

View File

@ -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_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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_

File diff suppressed because it is too large Load Diff

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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);

View File

@ -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("");

View File

@ -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);

View File

@ -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();

View File

@ -0,0 +1,5 @@
// |jit-test| error: ReferenceError
(function(x) {
x = i ? 4 : 2
y
})()

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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) {

View File

@ -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:

View File

@ -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);

View File

@ -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.

View File

@ -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);

View File

@ -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__);

View File

@ -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)

97
js/src/jit/Label.h Normal file
View File

@ -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

View File

@ -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)
{

View File

@ -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 {

View File

@ -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_;

View File

@ -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()
{

View File

@ -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();

View File

@ -384,6 +384,7 @@ ParallelSafetyAnalysis::analyze()
// always bailout.
if (*block == graph_.entryBlock()) {
Spew(SpewCompile, "Entry block contains unsafe MIR");
mir_->disable();
return false;
}

View File

@ -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
{

View File

@ -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:

View File

@ -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)
{

View File

@ -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);
}

Some files were not shown because too many files have changed in this diff Show More