From 060f8f485423f70d0e2363db74373c9065207425 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 13 Mar 2013 15:15:05 -0400 Subject: [PATCH 001/202] Bug 845592 - Adjust the New Window menu entries in permanent private browsing mode; r=gavin --HG-- extra : rebase_source : c2bda2726ec2d8f63c20b69c5b7718f1403d619c --- browser/base/content/browser.js | 19 +++++++- .../test/browser_private_browsing_window.js | 43 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 3b3b3937010..be24637649c 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -7198,7 +7198,6 @@ let gPrivateBrowsingUI = { // temporary fix until bug 463607 is fixed document.getElementById("Tools:Sanitize").setAttribute("disabled", "true"); - // Adjust the window's title if (window.location.href == getBrowserURL()) { #ifdef XP_MACOSX if (!PrivateBrowsingUtils.permanentPrivateBrowsing) { @@ -7206,6 +7205,7 @@ let gPrivateBrowsingUI = { } #endif + // Adjust the window's title let docElement = document.documentElement; docElement.setAttribute("title", docElement.getAttribute("title_privatebrowsing")); @@ -7214,6 +7214,23 @@ let gPrivateBrowsingUI = { docElement.setAttribute("privatebrowsingmode", PrivateBrowsingUtils.permanentPrivateBrowsing ? "permanent" : "temporary"); gBrowser.updateTitlebar(); + + if (PrivateBrowsingUtils.permanentPrivateBrowsing) { + // Adjust the New Window menu entries + [ + { normal: "menu_newNavigator", private: "menu_newPrivateWindow" }, + { normal: "appmenu_newNavigator", private: "appmenu_newPrivateWindow" }, + ].forEach(function(menu) { + let newWindow = document.getElementById(menu.normal); + let newPrivateWindow = document.getElementById(menu.private); + if (newWindow && newPrivateWindow) { + newPrivateWindow.hidden = true; + newWindow.label = newPrivateWindow.label; + newWindow.accessKey = newPrivateWindow.accessKey; + newWindow.command = newPrivateWindow.command; + } + }); + } } if (gURLBar) { diff --git a/browser/base/content/test/browser_private_browsing_window.js b/browser/base/content/test/browser_private_browsing_window.js index d71f98e27f5..607a34060c8 100644 --- a/browser/base/content/test/browser_private_browsing_window.js +++ b/browser/base/content/test/browser_private_browsing_window.js @@ -16,9 +16,50 @@ function test() { whenDelayedStartupFinished(privateWin, function() { nonPrivateWin = privateWin.OpenBrowserWindow({private: false}); ok(!PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), "privateWin.OpenBrowserWindow({private: false}) should open a normal window"); + nonPrivateWin.close(); + + [ + { normal: "menu_newNavigator", private: "menu_newPrivateWindow", accesskey: true }, + { normal: "appmenu_newNavigator", private: "appmenu_newPrivateWindow", accesskey: false }, + ].forEach(function(menu) { + let newWindow = privateWin.document.getElementById(menu.normal); + let newPrivateWindow = privateWin.document.getElementById(menu.private); + if (newWindow && newPrivateWindow) { + ok(!newPrivateWindow.hidden, "New Private Window menu item should be hidden"); + isnot(newWindow.label, newPrivateWindow.label, "New Window's label shouldn't be overwritten"); + if (menu.accesskey) { + isnot(newWindow.accessKey, newPrivateWindow.accessKey, "New Window's accessKey shouldn't be overwritten"); + } + isnot(newWindow.command, newPrivateWindow.command, "New Window's command shouldn't be overwritten"); + } + }); + privateWin.close(); - finish(); + + Services.prefs.setBoolPref("browser.privatebrowsing.autostart", true); + privateWin = OpenBrowserWindow({private: true}); + whenDelayedStartupFinished(privateWin, function() { + [ + { normal: "menu_newNavigator", private: "menu_newPrivateWindow", accessKey: true }, + { normal: "appmenu_newNavigator", private: "appmenu_newPrivateWindow", accessKey: false }, + ].forEach(function(menu) { + let newWindow = privateWin.document.getElementById(menu.normal); + let newPrivateWindow = privateWin.document.getElementById(menu.private); + if (newWindow && newPrivateWindow) { + ok(newPrivateWindow.hidden, "New Private Window menu item should be hidden"); + is(newWindow.label, newPrivateWindow.label, "New Window's label should be overwritten"); + if (menu.accesskey) { + is(newWindow.accessKey, newPrivateWindow.accessKey, "New Window's accessKey should be overwritten"); + } + is(newWindow.command, newPrivateWindow.command, "New Window's command should be overwritten"); + } + }); + + privateWin.close(); + Services.prefs.clearUserPref("browser.privatebrowsing.autostart"); + finish(); + }); }); } From 672e27d6bc5e118cd3d5cbe9b5fc1f38637e95e6 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 13 Mar 2013 23:44:49 -0400 Subject: [PATCH 002/202] Bug 850970 - Set the initial value of gain nodes on the audio stream to make sure that they don't mute playback; r=roc --HG-- extra : rebase_source : caf58ca903665f8b3014451926cec9772969663f --- content/media/AudioEventTimeline.h | 7 ------- content/media/webaudio/GainNode.cpp | 2 ++ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/content/media/AudioEventTimeline.h b/content/media/AudioEventTimeline.h index 1a5f0582458..dff1871c147 100644 --- a/content/media/AudioEventTimeline.h +++ b/content/media/AudioEventTimeline.h @@ -120,13 +120,6 @@ template class AudioEventTimeline { public: - // This constructor should only be used for objects which are meant to be - // copied from other properly constructed objects. - AudioEventTimeline() - : mValue(0.f) - { - } - explicit AudioEventTimeline(float aDefaultValue) : mValue(aDefaultValue) { diff --git a/content/media/webaudio/GainNode.cpp b/content/media/webaudio/GainNode.cpp index 6f0dac5ba96..2cf83972d67 100644 --- a/content/media/webaudio/GainNode.cpp +++ b/content/media/webaudio/GainNode.cpp @@ -46,6 +46,8 @@ public: explicit GainNodeEngine(AudioDestinationNode* aDestination) : mSource(nullptr) , mDestination(static_cast (aDestination->Stream())) + // Keep the default value in sync with the default value in GainNode::GainNode. + , mGain(1.f) { } From bada1afd495bf2d503f422450bf4349218249a38 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 14 Mar 2013 21:01:02 -0400 Subject: [PATCH 003/202] Bug 851338 - Implement AudioContext.currentTime; r=roc --HG-- extra : rebase_source : 064da119e26ca15e361c003a4fabe7d3fe77e256 --- content/media/webaudio/AudioContext.cpp | 6 +++++ content/media/webaudio/AudioContext.h | 2 ++ content/media/webaudio/test/Makefile.in | 1 + .../media/webaudio/test/test_currentTime.html | 27 +++++++++++++++++++ dom/webidl/AudioContext.webidl | 1 + 5 files changed, 37 insertions(+) create mode 100644 content/media/webaudio/test/test_currentTime.html diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index a1e827c19aa..d2b396a77be 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -191,5 +191,11 @@ AudioContext::DestinationStream() const return Destination()->Stream(); } +double +AudioContext::CurrentTime() const +{ + return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime()); +} + } } diff --git a/content/media/webaudio/AudioContext.h b/content/media/webaudio/AudioContext.h index 9d97ebc9013..3d2b9cb2803 100644 --- a/content/media/webaudio/AudioContext.h +++ b/content/media/webaudio/AudioContext.h @@ -78,6 +78,8 @@ public: return float(IdealAudioRate()); } + double CurrentTime() const; + AudioListener* Listener(); already_AddRefed CreateBufferSource(); diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in index bbd8c39b81d..bcabc1c03ad 100644 --- a/content/media/webaudio/test/Makefile.in +++ b/content/media/webaudio/test/Makefile.in @@ -21,6 +21,7 @@ MOCHITEST_FILES := \ test_AudioListener.html \ test_badConnect.html \ test_biquadFilterNode.html \ + test_currentTime.html \ test_delayNode.html \ test_decodeAudioData.html \ test_dynamicsCompressorNode.html \ diff --git a/content/media/webaudio/test/test_currentTime.html b/content/media/webaudio/test/test_currentTime.html new file mode 100644 index 00000000000..09ab141b345 --- /dev/null +++ b/content/media/webaudio/test/test_currentTime.html @@ -0,0 +1,27 @@ + + + + Test AudioContext.currentTime + + + + +
+
+
+ + diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl index a1b40a61e2e..736b6755ba9 100644 --- a/dom/webidl/AudioContext.webidl +++ b/dom/webidl/AudioContext.webidl @@ -18,6 +18,7 @@ interface AudioContext { readonly attribute AudioDestinationNode destination; readonly attribute float sampleRate; + readonly attribute double currentTime; readonly attribute AudioListener listener; [Creator, Throws] From 2266b4d8613b82d25a2461352f6d3aa4d84849fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Thu, 14 Mar 2013 20:27:43 -0700 Subject: [PATCH 004/202] Bug 849988 - Implement support for a removable property for preinstalled apps to define if the app can be uninstalled or not r=ferjm --- dom/apps/src/AppsServiceChild.jsm | 10 +++------- dom/apps/src/AppsUtils.jsm | 25 +++++++++++++++++++++++++ dom/apps/src/Makefile.in | 2 +- dom/apps/src/Webapps.jsm | 13 ++++--------- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/dom/apps/src/AppsServiceChild.jsm b/dom/apps/src/AppsServiceChild.jsm index a4ad71e053d..06682ab279c 100644 --- a/dom/apps/src/AppsServiceChild.jsm +++ b/dom/apps/src/AppsServiceChild.jsm @@ -103,6 +103,7 @@ this.DOMApplicationRegistry = { debug("getAppFromObserverMessage " + aMessage); return AppsUtils.getAppFromObserverMessage(this.webapps. aMessage); }, + getCoreAppsBasePath: function getCoreAppsBasePath() { debug("getCoreAppsBasePath() not yet supported on child!"); return null; @@ -114,13 +115,8 @@ this.DOMApplicationRegistry = { }, getAppInfo: function getAppInfo(aAppId) { - if (!this.webapps[aAppId]) { - debug("No webapp for " + aAppId); - return null; - } - return { "basePath": this.webapps[aAppId].basePath + "/", - "isCoreApp": !this.webapps[aAppId].removable }; - }, + return AppsUtils.getAppInfo(this.webapps, aAppId); + } } DOMApplicationRegistry.init(); diff --git a/dom/apps/src/AppsUtils.jsm b/dom/apps/src/AppsUtils.jsm index 01a4e30082c..cf4ad908395 100644 --- a/dom/apps/src/AppsUtils.jsm +++ b/dom/apps/src/AppsUtils.jsm @@ -11,6 +11,7 @@ const Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { return Cc["@mozilla.org/network/util;1"] @@ -176,6 +177,30 @@ this.AppsUtils = { return null; }, + getCoreAppsBasePath: function getCoreAppsBasePath() { + debug("getCoreAppsBasePath()"); + return FileUtils.getDir("coreAppsDir", ["webapps"], false).path; + }, + + getAppInfo: function getAppInfo(aApps, aAppId) { + if (!aApps[aAppId]) { + debug("No webapp for " + aAppId); + return null; + } + + // We can have 3rd party apps that are non-removable, + // so we can't use the 'removable' property for isCoreApp + // Instead, we check if the app is installed under /system/b2g + let isCoreApp = false; + let app = aApps[aAppId]; +#ifdef MOZ_WIDGET_GONK + isCoreApp = app.basePath == this.getCoreAppsBasePath(); +#endif + debug(app.name + " isCoreApp: " + isCoreApp); + return { "basePath": app.basePath + "/", + "isCoreApp": isCoreApp }; + }, + /** * From https://developer.mozilla.org/en/OpenWebApps/The_Manifest * Only the name property is mandatory. diff --git a/dom/apps/src/Makefile.in b/dom/apps/src/Makefile.in index ce88eedeefc..873ec1d0169 100644 --- a/dom/apps/src/Makefile.in +++ b/dom/apps/src/Makefile.in @@ -21,11 +21,11 @@ EXTRA_PP_COMPONENTS = \ EXTRA_PP_JS_MODULES += \ Webapps.jsm \ + AppsUtils.jsm \ $(NULL) EXTRA_JS_MODULES += \ AppsServiceChild.jsm \ - AppsUtils.jsm \ OfflineCacheInstaller.jsm \ PermissionsInstaller.jsm \ PermissionsTable.jsm \ diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index a57591e80ea..b3db26d51e3 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -283,7 +283,6 @@ this.DOMApplicationRegistry = { } app.origin = "app://" + aId; - app.removable = true; // Extract the manifest.webapp file from application.zip. let zipFile = baseDir.clone(); @@ -861,12 +860,7 @@ this.DOMApplicationRegistry = { }, getAppInfo: function getAppInfo(aAppId) { - if (!this.webapps[aAppId]) { - debug("No webapp for " + aAppId); - return null; - } - return { "basePath": this.webapps[aAppId].basePath + "/", - "isCoreApp": !this.webapps[aAppId].removable }; + return AppsUtils.getAppInfo(this.webapps, aAppId); }, // Some messages can be listened by several content processes: @@ -2027,7 +2021,8 @@ this.DOMApplicationRegistry = { } // the manifest file used to be named manifest.json, so fallback on this. - let baseDir = (this.webapps[id].removable ? DIRECTORY_NAME : "coreAppsDir"); + let baseDir = this.webapps[id].basePath == this.getCoreAppsBasePath() + ? "coreAppsDir" : DIRECTORY_NAME; let file = FileUtils.getFile(baseDir, ["webapps", id, "manifest.webapp"], true); if (!file.exists()) { file = FileUtils.getFile(baseDir, ["webapps", id, "update.webapp"], true); @@ -2661,7 +2656,7 @@ this.DOMApplicationRegistry = { }, getCoreAppsBasePath: function() { - return FileUtils.getDir("coreAppsDir", ["webapps"], false).path; + return AppsUtils.getCoreAppsBasePath(); }, getWebAppsBasePath: function getWebAppsBasePath() { From 327a511bd4f49efc653875de9540d643bf1157d6 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Fri, 15 Mar 2013 16:28:07 +1300 Subject: [PATCH 005/202] Bug 850713 - Bump the required NDK version to 9. r=blassey.bugs,mh+mozilla --HG-- extra : rebase_source : d6886dcfe87747e77c3828289b1184a58e6a0c09 --- build/autoconf/android.m4 | 12 ++---------- configure.in | 2 +- js/src/build/autoconf/android.m4 | 12 ++---------- mobile/android/config/mozconfigs/android-armv6/debug | 2 +- .../config/mozconfigs/android-armv6/l10n-nightly | 2 +- .../config/mozconfigs/android-armv6/l10n-release | 2 +- .../android/config/mozconfigs/android-armv6/nightly | 2 +- .../android/config/mozconfigs/android-armv6/release | 2 +- .../android/config/mozconfigs/android-noion/nightly | 2 +- mobile/android/config/mozconfigs/android/debug | 2 +- .../android/config/mozconfigs/android/l10n-nightly | 2 +- .../android/config/mozconfigs/android/l10n-release | 2 +- mobile/android/config/mozconfigs/android/nightly | 2 +- mobile/android/config/mozconfigs/android/release | 2 +- 14 files changed, 16 insertions(+), 32 deletions(-) diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 index f7a82272ae9..aa18b4dd565 100644 --- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -26,19 +26,11 @@ MOZ_ARG_ENABLE_BOOL(android-libstdcxx, MOZ_ANDROID_LIBSTDCXX=1, MOZ_ANDROID_LIBSTDCXX= ) -dnl default android_version is different per target cpu -case "$target_cpu" in -arm) - android_version=5 - ;; -i?86|mipsel) - android_version=9 - ;; -esac +android_version=9 MOZ_ARG_WITH_STRING(android-version, [ --with-android-version=VER - android platform version, default 5 for arm, 9 for x86/mips], + android platform version, default 9], android_version=$withval) MOZ_ARG_WITH_STRING(android-platform, diff --git a/configure.in b/configure.in index e3ebd1eaa02..e13437fd574 100644 --- a/configure.in +++ b/configure.in @@ -9268,7 +9268,7 @@ if test -n "$_WRAP_MALLOC"; then fi if test -z "$MOZ_NATIVE_NSPR"; then - ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla" + ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla --with-android-version=$ANDROID_VERSION" if test -z "$MOZ_DEBUG"; then ac_configure_args="$ac_configure_args --disable-debug" else diff --git a/js/src/build/autoconf/android.m4 b/js/src/build/autoconf/android.m4 index f7a82272ae9..aa18b4dd565 100644 --- a/js/src/build/autoconf/android.m4 +++ b/js/src/build/autoconf/android.m4 @@ -26,19 +26,11 @@ MOZ_ARG_ENABLE_BOOL(android-libstdcxx, MOZ_ANDROID_LIBSTDCXX=1, MOZ_ANDROID_LIBSTDCXX= ) -dnl default android_version is different per target cpu -case "$target_cpu" in -arm) - android_version=5 - ;; -i?86|mipsel) - android_version=9 - ;; -esac +android_version=9 MOZ_ARG_WITH_STRING(android-version, [ --with-android-version=VER - android platform version, default 5 for arm, 9 for x86/mips], + android platform version, default 9], android_version=$withval) MOZ_ARG_WITH_STRING(android-platform, diff --git a/mobile/android/config/mozconfigs/android-armv6/debug b/mobile/android/config/mozconfigs/android-armv6/debug index b167e8469d8..1881e203d64 100644 --- a/mobile/android/config/mozconfigs/android-armv6/debug +++ b/mobile/android/config/mozconfigs/android-armv6/debug @@ -12,7 +12,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib # IonMonkey disabled in bug 789373 diff --git a/mobile/android/config/mozconfigs/android-armv6/l10n-nightly b/mobile/android/config/mozconfigs/android-armv6/l10n-nightly index 3ee1d89caec..f452220ed72 100644 --- a/mobile/android/config/mozconfigs/android-armv6/l10n-nightly +++ b/mobile/android/config/mozconfigs/android-armv6/l10n-nightly @@ -18,7 +18,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/l10n-release b/mobile/android/config/mozconfigs/android-armv6/l10n-release index 218e320c138..71a141106cc 100644 --- a/mobile/android/config/mozconfigs/android-armv6/l10n-release +++ b/mobile/android/config/mozconfigs/android-armv6/l10n-release @@ -15,7 +15,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/nightly b/mobile/android/config/mozconfigs/android-armv6/nightly index 91b4d37e7f0..7ca7e29f82b 100644 --- a/mobile/android/config/mozconfigs/android-armv6/nightly +++ b/mobile/android/config/mozconfigs/android-armv6/nightly @@ -9,7 +9,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/release b/mobile/android/config/mozconfigs/android-armv6/release index 0587d54478b..902b10707a8 100644 --- a/mobile/android/config/mozconfigs/android-armv6/release +++ b/mobile/android/config/mozconfigs/android-armv6/release @@ -9,7 +9,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-noion/nightly b/mobile/android/config/mozconfigs/android-noion/nightly index c12bf30f690..97529ad095d 100644 --- a/mobile/android/config/mozconfigs/android-noion/nightly +++ b/mobile/android/config/mozconfigs/android-noion/nightly @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/debug b/mobile/android/config/mozconfigs/android/debug index 29593209d9c..188c56852d2 100644 --- a/mobile/android/config/mozconfigs/android/debug +++ b/mobile/android/config/mozconfigs/android/debug @@ -11,7 +11,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib export JAVA_HOME=/tools/jdk6 diff --git a/mobile/android/config/mozconfigs/android/l10n-nightly b/mobile/android/config/mozconfigs/android/l10n-nightly index 12b2722c5d1..8971688bc03 100644 --- a/mobile/android/config/mozconfigs/android/l10n-nightly +++ b/mobile/android/config/mozconfigs/android/l10n-nightly @@ -17,7 +17,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/l10n-release b/mobile/android/config/mozconfigs/android/l10n-release index 18ac37ab1db..cf37d2b6ffe 100644 --- a/mobile/android/config/mozconfigs/android/l10n-release +++ b/mobile/android/config/mozconfigs/android/l10n-release @@ -14,7 +14,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/nightly b/mobile/android/config/mozconfigs/android/nightly index adced2ff9e9..95cd28160a3 100644 --- a/mobile/android/config/mozconfigs/android/nightly +++ b/mobile/android/config/mozconfigs/android/nightly @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/release b/mobile/android/config/mozconfigs/android/release index 6533b7f908d..8b11fe2ff46 100644 --- a/mobile/android/config/mozconfigs/android/release +++ b/mobile/android/config/mozconfigs/android/release @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} From be8a51d795c36167a98366dd536b0c17049cbb3c Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 13 Mar 2013 19:36:46 +0100 Subject: [PATCH 006/202] Bug 698328 - Add a new cubeb backend based on AudioTrack.cpp. r=kinetik --HG-- extra : rebase_source : ec59b20cc746e052c1627285929138f34a349c00 --- configure.in | 5 +- media/libcubeb/AUTHORS | 1 + media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/include/cubeb.h | 8 +- media/libcubeb/src/Makefile.in | 9 +- .../src/android/audiotrack_definitions.h | 81 ++++ media/libcubeb/src/android/sles_definitions.h | 67 +++ media/libcubeb/src/audiotrack_definitions.h | 81 ++++ media/libcubeb/src/cubeb-internal.h | 4 +- media/libcubeb/src/cubeb.c | 40 +- media/libcubeb/src/cubeb_audiotrack.c | 402 ++++++++++++++++++ media/libcubeb/src/cubeb_opensl.c | 3 + media/libcubeb/src/cubeb_sndio.c | 2 +- media/libcubeb/src/cubeb_winmm.c | 2 +- media/libcubeb/update.sh | 3 + 15 files changed, 677 insertions(+), 33 deletions(-) create mode 100644 media/libcubeb/src/android/audiotrack_definitions.h create mode 100644 media/libcubeb/src/android/sles_definitions.h create mode 100644 media/libcubeb/src/audiotrack_definitions.h create mode 100644 media/libcubeb/src/cubeb_audiotrack.c diff --git a/configure.in b/configure.in index e13437fd574..21c264afee1 100644 --- a/configure.in +++ b/configure.in @@ -5657,10 +5657,7 @@ fi if test -n "$MOZ_CUBEB"; then case "$target" in *-android*|*-linuxandroid*) - if test -n "$gonkdir"; then - AC_DEFINE(MOZ_CUBEB) - fi - dnl No Android implementation of libcubeb yet. + AC_DEFINE(MOZ_CUBEB) ;; *-linux*) AC_DEFINE(MOZ_CUBEB) diff --git a/media/libcubeb/AUTHORS b/media/libcubeb/AUTHORS index 74d9c7d8127..d98205cb4d9 100644 --- a/media/libcubeb/AUTHORS +++ b/media/libcubeb/AUTHORS @@ -1,3 +1,4 @@ Matthew Gregan Alexandre Ratchov Michael Wu +Paul Adenot diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index 7232df15862..bed89182d1d 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was f2d524c34b6f75d85053034c3075c2ff08383769. +The git commit ID used was 0c7d97523096a7d4ae363974393d06f77c4592c9. diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h index 4494a6eb304..bc398e0d1b9 100644 --- a/media/libcubeb/include/cubeb.h +++ b/media/libcubeb/include/cubeb.h @@ -4,12 +4,12 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -#ifndef CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 -#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 +#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382) +#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 #include -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { #endif @@ -226,7 +226,7 @@ int cubeb_stream_stop(cubeb_stream * stream); @retval CUBEB_ERROR */ int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position); -#ifdef __cplusplus +#if defined(__cplusplus) } #endif diff --git a/media/libcubeb/src/Makefile.in b/media/libcubeb/src/Makefile.in index 5b06a460026..9d6d2366220 100644 --- a/media/libcubeb/src/Makefile.in +++ b/media/libcubeb/src/Makefile.in @@ -28,14 +28,17 @@ DEFINES += -DUSE_WINMM endif ifeq ($(OS_TARGET),Android) -ifeq ($(MOZ_WIDGET_TOOLKIT),gonk) +ifneq ($(MOZ_WIDGET_TOOLKIT),gonk) +CSRCS += \ + cubeb_audiotrack.c \ + $(NULL) +DEFINES += -DUSE_AUDIOTRACK +endif CSRCS += \ cubeb_opensl.c \ $(NULL) DEFINES += -DUSE_OPENSL endif -# No Android implementation of libcubeb yet. -endif ifeq ($(OS_TARGET),Darwin) CSRCS += \ diff --git a/media/libcubeb/src/android/audiotrack_definitions.h b/media/libcubeb/src/android/audiotrack_definitions.h new file mode 100644 index 00000000000..da3032d4084 --- /dev/null +++ b/media/libcubeb/src/android/audiotrack_definitions.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* + * The following definitions are copied from the android sources. Only the + * relevant enum member and values needed are copied. + */ + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h + */ +typedef int32_t status_t; + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h + */ +struct Buffer { + uint32_t flags; + int channelCount; + int format; + size_t frameCount; + size_t size; + union { + void* raw; + short* i16; + int8_t* i8; + }; +}; + +enum event_type { + EVENT_MORE_DATA = 0, + EVENT_UNDERRUN = 1, + EVENT_LOOP_END = 2, + EVENT_MARKER = 3, + EVENT_NEW_POS = 4, + EVENT_BUFFER_END = 5 +}; + +/** + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h + * and + * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h + */ + +#define AUDIO_STREAM_TYPE_MUSIC 3 + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2, + AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, + AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) +} AudioTrack_ChannelMapping_ICS; + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo = 0x4, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo = 0x8, + AUDIO_CHANNEL_OUT_MONO_Froyo = AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo, + AUDIO_CHANNEL_OUT_STEREO_Froyo = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo) +} AudioTrack_ChannelMapping_Froyo; + +typedef enum { + AUDIO_FORMAT_PCM = 0x00000000, + AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, + AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), +} AudioTrack_SampleType; + diff --git a/media/libcubeb/src/android/sles_definitions.h b/media/libcubeb/src/android/sles_definitions.h new file mode 100644 index 00000000000..f5670c131c2 --- /dev/null +++ b/media/libcubeb/src/android/sles_definitions.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file is similar to the file "OpenSLES_AndroidConfiguration.h" found in + * the Android NDK, but removes the #ifdef __cplusplus defines, so we can keep + * using a C compiler in cubeb. + */ + +#ifndef OPENSL_ES_ANDROIDCONFIGURATION_H_ +#define OPENSL_ES_ANDROIDCONFIGURATION_H_ + +/*---------------------------------------------------------------------------*/ +/* Android AudioRecorder configuration */ +/*---------------------------------------------------------------------------*/ + +/** Audio recording preset */ +/** Audio recording preset key */ +#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset") +/** Audio recording preset values */ +/** preset "none" cannot be set, it is used to indicate the current settings + * do not match any of the presets. */ +#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32) 0x00000000) +/** generic recording configuration on the platform */ +#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32) 0x00000001) +/** uses the microphone audio source with the same orientation as the camera + * if available, the main device microphone otherwise */ +#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32) 0x00000002) +/** uses the main microphone tuned for voice recognition */ +#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32) 0x00000003) + +/*---------------------------------------------------------------------------*/ +/* Android AudioPlayer configuration */ +/*---------------------------------------------------------------------------*/ + +/** Audio playback stream type */ +/** Audio playback stream type key */ +#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType") + +/** Audio playback stream type values */ +/* same as android.media.AudioManager.STREAM_VOICE_CALL */ +#define SL_ANDROID_STREAM_VOICE ((SLint32) 0x00000000) +/* same as android.media.AudioManager.STREAM_SYSTEM */ +#define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001) +/* same as android.media.AudioManager.STREAM_RING */ +#define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002) +/* same as android.media.AudioManager.STREAM_MUSIC */ +#define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003) +/* same as android.media.AudioManager.STREAM_ALARM */ +#define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004) +/* same as android.media.AudioManager.STREAM_NOTIFICATION */ +#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005) + +#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */ diff --git a/media/libcubeb/src/audiotrack_definitions.h b/media/libcubeb/src/audiotrack_definitions.h new file mode 100644 index 00000000000..da3032d4084 --- /dev/null +++ b/media/libcubeb/src/audiotrack_definitions.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* + * The following definitions are copied from the android sources. Only the + * relevant enum member and values needed are copied. + */ + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h + */ +typedef int32_t status_t; + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h + */ +struct Buffer { + uint32_t flags; + int channelCount; + int format; + size_t frameCount; + size_t size; + union { + void* raw; + short* i16; + int8_t* i8; + }; +}; + +enum event_type { + EVENT_MORE_DATA = 0, + EVENT_UNDERRUN = 1, + EVENT_LOOP_END = 2, + EVENT_MARKER = 3, + EVENT_NEW_POS = 4, + EVENT_BUFFER_END = 5 +}; + +/** + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h + * and + * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h + */ + +#define AUDIO_STREAM_TYPE_MUSIC 3 + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2, + AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, + AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) +} AudioTrack_ChannelMapping_ICS; + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo = 0x4, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo = 0x8, + AUDIO_CHANNEL_OUT_MONO_Froyo = AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo, + AUDIO_CHANNEL_OUT_STEREO_Froyo = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo) +} AudioTrack_ChannelMapping_Froyo; + +typedef enum { + AUDIO_FORMAT_PCM = 0x00000000, + AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, + AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), +} AudioTrack_SampleType; + diff --git a/media/libcubeb/src/cubeb-internal.h b/media/libcubeb/src/cubeb-internal.h index ec2ddfaf977..f617a56adf0 100644 --- a/media/libcubeb/src/cubeb-internal.h +++ b/media/libcubeb/src/cubeb-internal.h @@ -4,8 +4,8 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -#ifndef CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 -#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 +#if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5) +#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 #include "cubeb/cubeb.h" diff --git a/media/libcubeb/src/cubeb.c b/media/libcubeb/src/cubeb.c index fface813247..a4b6a5329a1 100644 --- a/media/libcubeb/src/cubeb.c +++ b/media/libcubeb/src/cubeb.c @@ -5,7 +5,7 @@ * accompanying file LICENSE for details. */ #include -#ifdef HAVE_CONFIG_H +#if defined(HAVE_CONFIG_H) #include "config.h" #endif #include "cubeb/cubeb.h" @@ -21,30 +21,33 @@ struct cubeb_stream { struct cubeb * context; }; -#ifdef USE_PULSE +#if defined(USE_PULSE) int pulse_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_ALSA +#if defined(USE_ALSA) int alsa_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_AUDIOQUEUE +#if defined(USE_AUDIOQUEUE) int audioqueue_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_AUDIOUNIT +#if defined(USE_AUDIOUNIT) int audiounit_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_DIRECTSOUND +#if defined(USE_DIRECTSOUND) int directsound_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_WINMM +#if defined(USE_WINMM) int winmm_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_SNDIO +#if defined(USE_SNDIO) int sndio_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_OPENSL +#if defined(USE_OPENSL) int opensl_init(cubeb ** context, char const * context_name); #endif +#if defined(USE_AUDIOTRACK) +int audiotrack_init(cubeb ** context, char const * context_name); +#endif int validate_stream_params(cubeb_stream_params stream_params) @@ -78,29 +81,32 @@ int cubeb_init(cubeb ** context, char const * context_name) { int (* init[])(cubeb **, char const *) = { -#ifdef USE_PULSE +#if defined(USE_PULSE) pulse_init, #endif -#ifdef USE_ALSA +#if defined(USE_ALSA) alsa_init, #endif -#ifdef USE_AUDIOUNIT +#if defined(USE_AUDIOUNIT) audiounit_init, #endif -#ifdef USE_AUDIOQUEUE +#if defined(USE_AUDIOQUEUE) audioqueue_init, #endif -#ifdef USE_WINMM +#if defined(USE_WINMM) winmm_init, #endif -#ifdef USE_DIRECTSOUND +#if defined(USE_DIRECTSOUND) directsound_init, #endif -#ifdef USE_SNDIO +#if defined(USE_SNDIO) sndio_init, #endif -#ifdef USE_OPENSL +#if defined(USE_OPENSL) opensl_init, +#endif +#if defined(USE_AUDIOTRACK) + audiotrack_init, #endif }; int i; diff --git a/media/libcubeb/src/cubeb_audiotrack.c b/media/libcubeb/src/cubeb_audiotrack.c new file mode 100644 index 00000000000..723dc947e37 --- /dev/null +++ b/media/libcubeb/src/cubeb_audiotrack.c @@ -0,0 +1,402 @@ +/* + * Copyright © 2013 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ + +#define NDEBUG +#include +#include +#include +#include +#include +#include "android/log.h" + +#include "cubeb/cubeb.h" +#include "cubeb-internal.h" +#include "android/audiotrack_definitions.h" + +#ifndef ALOG +#if defined(DEBUG) || defined(FORCE_ALOG) +#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args) +#else +#define ALOG(args...) +#endif +#endif + +/** + * A lot of bytes for safety. It should be possible to bring this down a bit. */ +#define SIZE_AUDIOTRACK_INSTANCE 256 + +/** + * call dlsym to get the symbol |mangled_name|, handle the error and store the + * pointer in |pointer|. Because depending on Android version, we want different + * symbols, not finding a symbol is not an error. */ +#define DLSYM_DLERROR(mangled_name, pointer, lib) \ + do { \ + pointer = dlsym(lib, mangled_name); \ + if (!pointer) { \ + ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \ + } else { \ + ALOG("%stm: OK", mangled_name); \ + } \ + } while(0); + +static struct cubeb_ops const audiotrack_ops; +void audiotrack_destroy(cubeb * context); +void audiotrack_stream_destroy(cubeb_stream * stream); + +struct AudioTrack { + /* only available on ICS and later. */ + /* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate); + /* if this symbol is not availble, and the next one is, we know + * we are on a Froyo (Android 2.2) device. */ + void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int); + void* (*ctor_froyo)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int); + void* (*dtor)(void* instance); + void (*start)(void* instance); + void (*pause)(void* instance); + uint32_t (*latency)(void* instance); + status_t (*check)(void* instance); + status_t (*get_position)(void* instance, uint32_t* position); + /* only used on froyo. */ + /* static */ int (*get_output_frame_count)(int* frame_count, int stream); + /* static */ int (*get_output_latency)(uint32_t* frame_count, int stream); + /* static */ int (*get_output_samplingrate)(int* frame_count, int stream); + status_t (*set_marker_position)(void* instance, unsigned int); + +}; + +struct cubeb { + struct cubeb_ops const * ops; + void * library; + struct AudioTrack klass; +}; + +struct cubeb_stream { + cubeb * context; + cubeb_stream_params params; + cubeb_data_callback data_callback; + cubeb_state_callback state_callback; + void * instance; + void * user_ptr; + /* Number of frames that have been passed to the AudioTrack callback */ + long unsigned written; + int draining; +}; + +static void +audiotrack_refill(int event, void* user, void* info) +{ + cubeb_stream * stream = user; + switch (event) { + case EVENT_MORE_DATA: { + long got = 0; + struct Buffer * b = (struct Buffer*)info; + + if (stream->draining) { + return; + } + + got = stream->data_callback(stream, stream->user_ptr, b->raw, b->frameCount); + + stream->written += got; + + if (got != (long)b->frameCount) { + uint32_t p; + stream->draining = 1; + /* set a marker so we are notified when the are done draining, that is, + * when every frame has been played by android. */ + stream->context->klass.set_marker_position(stream->instance, stream->written); + } + + break; + } + case EVENT_UNDERRUN: + ALOG("underrun in cubeb backend."); + break; + case EVENT_LOOP_END: + assert(0 && "We don't support the loop feature of audiotrack."); + break; + case EVENT_MARKER: + assert(stream->draining); + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); + break; + case EVENT_NEW_POS: + assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack."); + break; + case EVENT_BUFFER_END: + assert(0 && "Should not happen."); + break; + } +} + +/* We are running on froyo if we found the right AudioTrack constructor */ +static int +audiotrack_version_is_froyo(cubeb * ctx) +{ + return ctx->klass.ctor_froyo != NULL; +} + +int +audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count) +{ + status_t status; + /* Recent Android have a getMinFrameCount method. On Froyo, we have to compute it by hand. */ + if (audiotrack_version_is_froyo(ctx)) { + int samplerate, frame_count, latency, min_buffer_count; + status = ctx->klass.get_output_frame_count(&frame_count, AUDIO_STREAM_TYPE_MUSIC); + if (status) { + ALOG("error getting the output frame count."); + return CUBEB_ERROR; + } + status = ctx->klass.get_output_latency((uint32_t*)&latency, AUDIO_STREAM_TYPE_MUSIC); + if (status) { + ALOG("error getting the output frame count."); + return CUBEB_ERROR; + } + status = ctx->klass.get_output_samplingrate(&samplerate, AUDIO_STREAM_TYPE_MUSIC); + if (status) { + ALOG("error getting the output frame count."); + return CUBEB_ERROR; + } + + /* Those numbers were found reading the Android source. It is the minimum + * numbers that will be accepted by the AudioTrack class, hence yielding the + * best latency possible. + * See https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/media/libmedia/AudioTrack.cpp + * around line 181 for Android 2.2 */ + min_buffer_count = latency / ((1000 * frame_count) / samplerate); + min_buffer_count = min_buffer_count < 2 ? min_buffer_count : 2; + *min_frame_count = (frame_count * params->rate * min_buffer_count) / samplerate; + return CUBEB_OK; + } + /* Recent Android have a getMinFrameCount method. */ + status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate); + if (status != 0) { + ALOG("error getting the min frame count"); + return CUBEB_ERROR; + } + return CUBEB_OK; +} + +int +audiotrack_init(cubeb ** context, char const * context_name) +{ + cubeb * ctx; + struct AudioTrack* c; + + assert(context); + *context = NULL; + + ctx = calloc(1, sizeof(*ctx)); + assert(ctx); + + /* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android + * 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on + * the first call to a dlsym'ed function. Somehow this does not happen when + * using only the name of the library. */ + ctx->library = dlopen("libmedia.so", RTLD_LAZY); + if (!ctx->library) { + ALOG("dlopen error: %s.", dlerror()); + free(ctx); + return CUBEB_ERROR; + } + + /* Recent Android first, then Froyo. */ + DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library); + if (!ctx->klass.ctor) { + DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i", ctx->klass.ctor_froyo, ctx->library); + assert(ctx->klass.ctor_froyo); + } + DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library); + + DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library); + DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library); + + /* |getMinFrameCount| is not available on Froyo. */ + if (audiotrack_version_is_froyo(ctx)) { + DLSYM_DLERROR("_ZN7android11AudioSystem19getOutputFrameCountEPii", ctx->klass.get_output_frame_count, ctx->library); + DLSYM_DLERROR("_ZN7android11AudioSystem16getOutputLatencyEPji", ctx->klass.get_output_latency, ctx->library); + DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library); + } else { + DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); + } + + DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library); + + /* check that we have a combination of symbol that makes sense */ + c = &ctx->klass; + if(!((c->ctor || c->ctor_froyo) && /* at least on ctor. */ + c->dtor && c->latency && c->check && + /* at least one way to get the minimum frame count to request. */ + ((c->get_output_frame_count && c->get_output_latency && c->get_output_samplingrate) || + c->get_min_frame_count) && + c->start && c->pause && c->get_position && c->set_marker_position)) { + ALOG("Could not find all the symbols we need."); + audiotrack_destroy(ctx); + return CUBEB_ERROR; + } + + ctx->ops = &audiotrack_ops; + + *context = ctx; + + return CUBEB_OK; +} + +char const * +audiotrack_get_backend_id(cubeb * context) +{ + return "audiotrack"; +} + +void +audiotrack_destroy(cubeb * context) +{ + assert(context); + + dlclose(context->library); + + free(context); +} + +int +audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, + cubeb_stream_params stream_params, unsigned int latency, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, + void * user_ptr) +{ + struct cubeb_stream * stm; + int32_t channels; + int32_t min_frame_count; + + assert(ctx && stream); + + if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE || + stream_params.format == CUBEB_SAMPLE_FLOAT32BE) { + return CUBEB_ERROR_INVALID_FORMAT; + } + + if (audiotrack_get_min_frame_count(ctx, &stream_params, &min_frame_count)) { + return CUBEB_ERROR; + } + + stm = calloc(1, sizeof(*stm)); + assert(stm); + + stm->context = ctx; + stm->data_callback = data_callback; + stm->state_callback = state_callback; + stm->user_ptr = user_ptr; + stm->params = stream_params; + + stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1); + (*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad; + assert(stm->instance && "cubeb: EOM"); + + if (audiotrack_version_is_froyo(ctx)) { + channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Froyo : AUDIO_CHANNEL_OUT_MONO_Froyo; + } else { + channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS; + } + + if (audiotrack_version_is_froyo(ctx)) { + ctx->klass.ctor_froyo(stm->instance, + AUDIO_STREAM_TYPE_MUSIC, + stm->params.rate, + AUDIO_FORMAT_PCM_16_BIT, + channels, + min_frame_count, + 0, + audiotrack_refill, + stm, + 0); + } else { + ctx->klass.ctor(stm->instance, + AUDIO_STREAM_TYPE_MUSIC, + stm->params.rate, + AUDIO_FORMAT_PCM_16_BIT, + channels, + min_frame_count, + 0, + audiotrack_refill, + stm, + 0, + 0); + } + + assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad); + + if (ctx->klass.check(stm->instance)) { + ALOG("stream not initialized properly."); + audiotrack_stream_destroy(stm); + return CUBEB_ERROR; + } + + *stream = stm; + + return CUBEB_OK; +} + +void +audiotrack_stream_destroy(cubeb_stream * stream) +{ + assert(stream->context); + + stream->context->klass.dtor(stream->instance); + + free(stream->instance); + stream->instance = NULL; + free(stream); +} + +int +audiotrack_stream_start(cubeb_stream * stream) +{ + assert(stream->instance); + + stream->context->klass.start(stream->instance); + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); + + return CUBEB_OK; +} + +int +audiotrack_stream_stop(cubeb_stream * stream) +{ + assert(stream->instance); + + stream->context->klass.pause(stream->instance); + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); + + return CUBEB_OK; +} + +int +audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position) +{ + uint32_t p; + + assert(stream->instance && position); + stream->context->klass.get_position(stream->instance, &p); + *position = p; + + return CUBEB_OK; +} + +static struct cubeb_ops const audiotrack_ops = { + .init = audiotrack_init, + .get_backend_id = audiotrack_get_backend_id, + .destroy = audiotrack_destroy, + .stream_init = audiotrack_stream_init, + .stream_destroy = audiotrack_stream_destroy, + .stream_start = audiotrack_stream_start, + .stream_stop = audiotrack_stream_stop, + .stream_get_position = audiotrack_stream_get_position +}; diff --git a/media/libcubeb/src/cubeb_opensl.c b/media/libcubeb/src/cubeb_opensl.c index ffeca02f3b6..27c6a61a8c1 100644 --- a/media/libcubeb/src/cubeb_opensl.c +++ b/media/libcubeb/src/cubeb_opensl.c @@ -10,6 +10,7 @@ #include #include #if defined(__ANDROID__) +#include "android/sles_definitions.h" #include #endif #include "cubeb/cubeb.h" @@ -123,6 +124,8 @@ opensl_init(cubeb ** context, char const * context_name) ctx = calloc(1, sizeof(*ctx)); assert(ctx); + ctx->ops = &opensl_ops; + ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY); if (!ctx->lib) { free(ctx); diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c index d39290e94dc..49f7deed488 100644 --- a/media/libcubeb/src/cubeb_sndio.c +++ b/media/libcubeb/src/cubeb_sndio.c @@ -12,7 +12,7 @@ #include "cubeb/cubeb.h" #include "cubeb-internal.h" -#ifdef CUBEB_SNDIO_DEBUG +#if defined(CUBEB_SNDIO_DEBUG) #define DPR(...) fprintf(stderr, __VA_ARGS__); #else #define DPR(...) do {} while(0) diff --git a/media/libcubeb/src/cubeb_winmm.c b/media/libcubeb/src/cubeb_winmm.c index bd4e03ac6c6..e8b20bfb854 100644 --- a/media/libcubeb/src/cubeb_winmm.c +++ b/media/libcubeb/src/cubeb_winmm.c @@ -19,7 +19,7 @@ #include "cubeb-internal.h" /* This is missing from the MinGW headers. Use a safe fallback. */ -#ifndef MEMORY_ALLOCATION_ALIGNMENT +#if !defined(MEMORY_ALLOCATION_ALIGNMENT) #define MEMORY_ALLOCATION_ALIGNMENT 16 #endif diff --git a/media/libcubeb/update.sh b/media/libcubeb/update.sh index 403c4f13a04..f3cfc0e3d07 100644 --- a/media/libcubeb/update.sh +++ b/media/libcubeb/update.sh @@ -10,6 +10,9 @@ cp $1/src/cubeb_audiounit.c src cp $1/src/cubeb_pulse.c src cp $1/src/cubeb_sndio.c src cp $1/src/cubeb_opensl.c src +cp $1/src/cubeb_audiotrack.c src +cp $1/src/android/audiotrack_definitions.h src/android +cp $1/src/android/sles_definitions.h src/android cp $1/LICENSE . cp $1/README . cp $1/AUTHORS . From 5d94430aaa24675e176de12a9aab1645dd1c6906 Mon Sep 17 00:00:00 2001 From: Landry Breuil Date: Fri, 15 Mar 2013 16:28:07 +1300 Subject: [PATCH 007/202] Bug 851149 - Fix cubeb_sndio build after switchable-backends landing. r=kinetik --HG-- extra : rebase_source : 6130904c7e96e4a942eeaf87723d6766d70c3b55 --- media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/src/cubeb_sndio.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index bed89182d1d..8ca655eb959 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was 0c7d97523096a7d4ae363974393d06f77c4592c9. +The git commit ID used was f63060a67f4b4300b81b860a487b31cccb160ab0. diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c index 49f7deed488..c817f06ff6f 100644 --- a/media/libcubeb/src/cubeb_sndio.c +++ b/media/libcubeb/src/cubeb_sndio.c @@ -217,7 +217,7 @@ sndio_stream_init(cubeb *context, DPR("sndio_stream_init() unsupported params\n"); return CUBEB_ERROR_INVALID_FORMAT; } - sio_onmove(s->hdl, cubeb_onmove, s); + sio_onmove(s->hdl, sndio_onmove, s); s->active = 0; s->nfr = rpar.round; s->bpf = rpar.bps * rpar.pchan; @@ -262,7 +262,7 @@ sndio_stream_start(cubeb_stream *s) DPR("sndio_stream_start()\n"); s->active = 1; - err = pthread_create(&s->th, NULL, cubeb_mainloop, s); + err = pthread_create(&s->th, NULL, sndio_mainloop, s); if (err) { s->active = 0; return CUBEB_ERROR; From 971d74a7cb1ea615b82e27f924ddb02944b84086 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 14 Mar 2013 23:53:27 -0400 Subject: [PATCH 008/202] Backed out changeset a96fb85d42bf (bug 851338) because of Linux build failures Landed on a CLOSED TREE --- content/media/webaudio/AudioContext.cpp | 6 ----- content/media/webaudio/AudioContext.h | 2 -- content/media/webaudio/test/Makefile.in | 1 - .../media/webaudio/test/test_currentTime.html | 27 ------------------- dom/webidl/AudioContext.webidl | 1 - 5 files changed, 37 deletions(-) delete mode 100644 content/media/webaudio/test/test_currentTime.html diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index d2b396a77be..a1e827c19aa 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -191,11 +191,5 @@ AudioContext::DestinationStream() const return Destination()->Stream(); } -double -AudioContext::CurrentTime() const -{ - return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime()); -} - } } diff --git a/content/media/webaudio/AudioContext.h b/content/media/webaudio/AudioContext.h index 3d2b9cb2803..9d97ebc9013 100644 --- a/content/media/webaudio/AudioContext.h +++ b/content/media/webaudio/AudioContext.h @@ -78,8 +78,6 @@ public: return float(IdealAudioRate()); } - double CurrentTime() const; - AudioListener* Listener(); already_AddRefed CreateBufferSource(); diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in index bcabc1c03ad..bbd8c39b81d 100644 --- a/content/media/webaudio/test/Makefile.in +++ b/content/media/webaudio/test/Makefile.in @@ -21,7 +21,6 @@ MOCHITEST_FILES := \ test_AudioListener.html \ test_badConnect.html \ test_biquadFilterNode.html \ - test_currentTime.html \ test_delayNode.html \ test_decodeAudioData.html \ test_dynamicsCompressorNode.html \ diff --git a/content/media/webaudio/test/test_currentTime.html b/content/media/webaudio/test/test_currentTime.html deleted file mode 100644 index 09ab141b345..00000000000 --- a/content/media/webaudio/test/test_currentTime.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - Test AudioContext.currentTime - - - - -
-
-
- - diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl index 736b6755ba9..a1b40a61e2e 100644 --- a/dom/webidl/AudioContext.webidl +++ b/dom/webidl/AudioContext.webidl @@ -18,7 +18,6 @@ interface AudioContext { readonly attribute AudioDestinationNode destination; readonly attribute float sampleRate; - readonly attribute double currentTime; readonly attribute AudioListener listener; [Creator, Throws] From 0422ceeb14550c1f2bca26927a09b2a7ce3204c6 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 14 Mar 2013 21:01:02 -0400 Subject: [PATCH 009/202] Bug 851338 - Implement AudioContext.currentTime; r=roc Landed on a CLOSED TREE, because I'm mad at X11! --- content/media/DOMMediaStream.h | 1 + content/media/webaudio/AudioContext.cpp | 6 +++++ content/media/webaudio/AudioContext.h | 8 ++++++ content/media/webaudio/test/Makefile.in | 1 + .../media/webaudio/test/test_currentTime.html | 27 +++++++++++++++++++ dom/webidl/AudioContext.webidl | 1 + 6 files changed, 44 insertions(+) create mode 100644 content/media/webaudio/test/test_currentTime.html diff --git a/content/media/DOMMediaStream.h b/content/media/DOMMediaStream.h index 2b15570e29b..ce2946cea31 100644 --- a/content/media/DOMMediaStream.h +++ b/content/media/DOMMediaStream.h @@ -21,6 +21,7 @@ class nsXPCClassInfo; #undef GetCurrentTime #endif // X11 has a #define for CurrentTime. Unbelievable :-(. +// See content/media/webaudio/AudioContext.h for more fun! #ifdef CurrentTime #undef CurrentTime #endif diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index a1e827c19aa..d2b396a77be 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -191,5 +191,11 @@ AudioContext::DestinationStream() const return Destination()->Stream(); } +double +AudioContext::CurrentTime() const +{ + return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime()); +} + } } diff --git a/content/media/webaudio/AudioContext.h b/content/media/webaudio/AudioContext.h index 9d97ebc9013..0c7b2bbdb3e 100644 --- a/content/media/webaudio/AudioContext.h +++ b/content/media/webaudio/AudioContext.h @@ -21,6 +21,12 @@ #include "MediaStreamGraph.h" #include "nsIDOMWindow.h" +// X11 has a #define for CurrentTime. Unbelievable :-(. +// See content/media/DOMMediaStream.h for more fun! +#ifdef CurrentTime +#undef CurrentTime +#endif + struct JSContext; class JSObject; class nsIDOMWindow; @@ -78,6 +84,8 @@ public: return float(IdealAudioRate()); } + double CurrentTime() const; + AudioListener* Listener(); already_AddRefed CreateBufferSource(); diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in index bbd8c39b81d..bcabc1c03ad 100644 --- a/content/media/webaudio/test/Makefile.in +++ b/content/media/webaudio/test/Makefile.in @@ -21,6 +21,7 @@ MOCHITEST_FILES := \ test_AudioListener.html \ test_badConnect.html \ test_biquadFilterNode.html \ + test_currentTime.html \ test_delayNode.html \ test_decodeAudioData.html \ test_dynamicsCompressorNode.html \ diff --git a/content/media/webaudio/test/test_currentTime.html b/content/media/webaudio/test/test_currentTime.html new file mode 100644 index 00000000000..09ab141b345 --- /dev/null +++ b/content/media/webaudio/test/test_currentTime.html @@ -0,0 +1,27 @@ + + + + Test AudioContext.currentTime + + + + +
+
+
+ + diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl index a1b40a61e2e..736b6755ba9 100644 --- a/dom/webidl/AudioContext.webidl +++ b/dom/webidl/AudioContext.webidl @@ -18,6 +18,7 @@ interface AudioContext { readonly attribute AudioDestinationNode destination; readonly attribute float sampleRate; + readonly attribute double currentTime; readonly attribute AudioListener listener; [Creator, Throws] From 69c6475c270b7c1219438b2194b8413955c40d21 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 14 Mar 2013 21:19:40 -0700 Subject: [PATCH 010/202] Back out 0044e4c2dc53 (bug 851149), 3080f18ba53d (bug 698328), 0b147c3f682b (bug 850713) for Android bustage CLOSED TREE --- build/autoconf/android.m4 | 12 +- configure.in | 7 +- js/src/build/autoconf/android.m4 | 12 +- media/libcubeb/AUTHORS | 1 - media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/include/cubeb.h | 8 +- media/libcubeb/src/Makefile.in | 9 +- .../src/android/audiotrack_definitions.h | 81 ---- media/libcubeb/src/android/sles_definitions.h | 67 --- media/libcubeb/src/audiotrack_definitions.h | 81 ---- media/libcubeb/src/cubeb-internal.h | 4 +- media/libcubeb/src/cubeb.c | 40 +- media/libcubeb/src/cubeb_audiotrack.c | 402 ------------------ media/libcubeb/src/cubeb_opensl.c | 3 - media/libcubeb/src/cubeb_sndio.c | 6 +- media/libcubeb/src/cubeb_winmm.c | 2 +- media/libcubeb/update.sh | 3 - .../config/mozconfigs/android-armv6/debug | 2 +- .../mozconfigs/android-armv6/l10n-nightly | 2 +- .../mozconfigs/android-armv6/l10n-release | 2 +- .../config/mozconfigs/android-armv6/nightly | 2 +- .../config/mozconfigs/android-armv6/release | 2 +- .../config/mozconfigs/android-noion/nightly | 2 +- .../android/config/mozconfigs/android/debug | 2 +- .../config/mozconfigs/android/l10n-nightly | 2 +- .../config/mozconfigs/android/l10n-release | 2 +- .../android/config/mozconfigs/android/nightly | 2 +- .../android/config/mozconfigs/android/release | 2 +- 28 files changed, 67 insertions(+), 695 deletions(-) delete mode 100644 media/libcubeb/src/android/audiotrack_definitions.h delete mode 100644 media/libcubeb/src/android/sles_definitions.h delete mode 100644 media/libcubeb/src/audiotrack_definitions.h delete mode 100644 media/libcubeb/src/cubeb_audiotrack.c diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 index aa18b4dd565..f7a82272ae9 100644 --- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -26,11 +26,19 @@ MOZ_ARG_ENABLE_BOOL(android-libstdcxx, MOZ_ANDROID_LIBSTDCXX=1, MOZ_ANDROID_LIBSTDCXX= ) -android_version=9 +dnl default android_version is different per target cpu +case "$target_cpu" in +arm) + android_version=5 + ;; +i?86|mipsel) + android_version=9 + ;; +esac MOZ_ARG_WITH_STRING(android-version, [ --with-android-version=VER - android platform version, default 9], + android platform version, default 5 for arm, 9 for x86/mips], android_version=$withval) MOZ_ARG_WITH_STRING(android-platform, diff --git a/configure.in b/configure.in index 21c264afee1..e3ebd1eaa02 100644 --- a/configure.in +++ b/configure.in @@ -5657,7 +5657,10 @@ fi if test -n "$MOZ_CUBEB"; then case "$target" in *-android*|*-linuxandroid*) - AC_DEFINE(MOZ_CUBEB) + if test -n "$gonkdir"; then + AC_DEFINE(MOZ_CUBEB) + fi + dnl No Android implementation of libcubeb yet. ;; *-linux*) AC_DEFINE(MOZ_CUBEB) @@ -9265,7 +9268,7 @@ if test -n "$_WRAP_MALLOC"; then fi if test -z "$MOZ_NATIVE_NSPR"; then - ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla --with-android-version=$ANDROID_VERSION" + ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla" if test -z "$MOZ_DEBUG"; then ac_configure_args="$ac_configure_args --disable-debug" else diff --git a/js/src/build/autoconf/android.m4 b/js/src/build/autoconf/android.m4 index aa18b4dd565..f7a82272ae9 100644 --- a/js/src/build/autoconf/android.m4 +++ b/js/src/build/autoconf/android.m4 @@ -26,11 +26,19 @@ MOZ_ARG_ENABLE_BOOL(android-libstdcxx, MOZ_ANDROID_LIBSTDCXX=1, MOZ_ANDROID_LIBSTDCXX= ) -android_version=9 +dnl default android_version is different per target cpu +case "$target_cpu" in +arm) + android_version=5 + ;; +i?86|mipsel) + android_version=9 + ;; +esac MOZ_ARG_WITH_STRING(android-version, [ --with-android-version=VER - android platform version, default 9], + android platform version, default 5 for arm, 9 for x86/mips], android_version=$withval) MOZ_ARG_WITH_STRING(android-platform, diff --git a/media/libcubeb/AUTHORS b/media/libcubeb/AUTHORS index d98205cb4d9..74d9c7d8127 100644 --- a/media/libcubeb/AUTHORS +++ b/media/libcubeb/AUTHORS @@ -1,4 +1,3 @@ Matthew Gregan Alexandre Ratchov Michael Wu -Paul Adenot diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index 8ca655eb959..7232df15862 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was f63060a67f4b4300b81b860a487b31cccb160ab0. +The git commit ID used was f2d524c34b6f75d85053034c3075c2ff08383769. diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h index bc398e0d1b9..4494a6eb304 100644 --- a/media/libcubeb/include/cubeb.h +++ b/media/libcubeb/include/cubeb.h @@ -4,12 +4,12 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382) -#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 +#ifndef CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 +#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 #include -#if defined(__cplusplus) +#ifdef __cplusplus extern "C" { #endif @@ -226,7 +226,7 @@ int cubeb_stream_stop(cubeb_stream * stream); @retval CUBEB_ERROR */ int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position); -#if defined(__cplusplus) +#ifdef __cplusplus } #endif diff --git a/media/libcubeb/src/Makefile.in b/media/libcubeb/src/Makefile.in index 9d6d2366220..5b06a460026 100644 --- a/media/libcubeb/src/Makefile.in +++ b/media/libcubeb/src/Makefile.in @@ -28,17 +28,14 @@ DEFINES += -DUSE_WINMM endif ifeq ($(OS_TARGET),Android) -ifneq ($(MOZ_WIDGET_TOOLKIT),gonk) -CSRCS += \ - cubeb_audiotrack.c \ - $(NULL) -DEFINES += -DUSE_AUDIOTRACK -endif +ifeq ($(MOZ_WIDGET_TOOLKIT),gonk) CSRCS += \ cubeb_opensl.c \ $(NULL) DEFINES += -DUSE_OPENSL endif +# No Android implementation of libcubeb yet. +endif ifeq ($(OS_TARGET),Darwin) CSRCS += \ diff --git a/media/libcubeb/src/android/audiotrack_definitions.h b/media/libcubeb/src/android/audiotrack_definitions.h deleted file mode 100644 index da3032d4084..00000000000 --- a/media/libcubeb/src/android/audiotrack_definitions.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -/* - * The following definitions are copied from the android sources. Only the - * relevant enum member and values needed are copied. - */ - -/* - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h - */ -typedef int32_t status_t; - -/* - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h - */ -struct Buffer { - uint32_t flags; - int channelCount; - int format; - size_t frameCount; - size_t size; - union { - void* raw; - short* i16; - int8_t* i8; - }; -}; - -enum event_type { - EVENT_MORE_DATA = 0, - EVENT_UNDERRUN = 1, - EVENT_LOOP_END = 2, - EVENT_MARKER = 3, - EVENT_NEW_POS = 4, - EVENT_BUFFER_END = 5 -}; - -/** - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h - * and - * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h - */ - -#define AUDIO_STREAM_TYPE_MUSIC 3 - -enum { - AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2, - AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, - AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) -} AudioTrack_ChannelMapping_ICS; - -enum { - AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo = 0x4, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo = 0x8, - AUDIO_CHANNEL_OUT_MONO_Froyo = AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo, - AUDIO_CHANNEL_OUT_STEREO_Froyo = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo) -} AudioTrack_ChannelMapping_Froyo; - -typedef enum { - AUDIO_FORMAT_PCM = 0x00000000, - AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, - AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), -} AudioTrack_SampleType; - diff --git a/media/libcubeb/src/android/sles_definitions.h b/media/libcubeb/src/android/sles_definitions.h deleted file mode 100644 index f5670c131c2..00000000000 --- a/media/libcubeb/src/android/sles_definitions.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * This file is similar to the file "OpenSLES_AndroidConfiguration.h" found in - * the Android NDK, but removes the #ifdef __cplusplus defines, so we can keep - * using a C compiler in cubeb. - */ - -#ifndef OPENSL_ES_ANDROIDCONFIGURATION_H_ -#define OPENSL_ES_ANDROIDCONFIGURATION_H_ - -/*---------------------------------------------------------------------------*/ -/* Android AudioRecorder configuration */ -/*---------------------------------------------------------------------------*/ - -/** Audio recording preset */ -/** Audio recording preset key */ -#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset") -/** Audio recording preset values */ -/** preset "none" cannot be set, it is used to indicate the current settings - * do not match any of the presets. */ -#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32) 0x00000000) -/** generic recording configuration on the platform */ -#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32) 0x00000001) -/** uses the microphone audio source with the same orientation as the camera - * if available, the main device microphone otherwise */ -#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32) 0x00000002) -/** uses the main microphone tuned for voice recognition */ -#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32) 0x00000003) - -/*---------------------------------------------------------------------------*/ -/* Android AudioPlayer configuration */ -/*---------------------------------------------------------------------------*/ - -/** Audio playback stream type */ -/** Audio playback stream type key */ -#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType") - -/** Audio playback stream type values */ -/* same as android.media.AudioManager.STREAM_VOICE_CALL */ -#define SL_ANDROID_STREAM_VOICE ((SLint32) 0x00000000) -/* same as android.media.AudioManager.STREAM_SYSTEM */ -#define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001) -/* same as android.media.AudioManager.STREAM_RING */ -#define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002) -/* same as android.media.AudioManager.STREAM_MUSIC */ -#define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003) -/* same as android.media.AudioManager.STREAM_ALARM */ -#define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004) -/* same as android.media.AudioManager.STREAM_NOTIFICATION */ -#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005) - -#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */ diff --git a/media/libcubeb/src/audiotrack_definitions.h b/media/libcubeb/src/audiotrack_definitions.h deleted file mode 100644 index da3032d4084..00000000000 --- a/media/libcubeb/src/audiotrack_definitions.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -/* - * The following definitions are copied from the android sources. Only the - * relevant enum member and values needed are copied. - */ - -/* - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h - */ -typedef int32_t status_t; - -/* - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h - */ -struct Buffer { - uint32_t flags; - int channelCount; - int format; - size_t frameCount; - size_t size; - union { - void* raw; - short* i16; - int8_t* i8; - }; -}; - -enum event_type { - EVENT_MORE_DATA = 0, - EVENT_UNDERRUN = 1, - EVENT_LOOP_END = 2, - EVENT_MARKER = 3, - EVENT_NEW_POS = 4, - EVENT_BUFFER_END = 5 -}; - -/** - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h - * and - * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h - */ - -#define AUDIO_STREAM_TYPE_MUSIC 3 - -enum { - AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2, - AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, - AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) -} AudioTrack_ChannelMapping_ICS; - -enum { - AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo = 0x4, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo = 0x8, - AUDIO_CHANNEL_OUT_MONO_Froyo = AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo, - AUDIO_CHANNEL_OUT_STEREO_Froyo = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo) -} AudioTrack_ChannelMapping_Froyo; - -typedef enum { - AUDIO_FORMAT_PCM = 0x00000000, - AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, - AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), -} AudioTrack_SampleType; - diff --git a/media/libcubeb/src/cubeb-internal.h b/media/libcubeb/src/cubeb-internal.h index f617a56adf0..ec2ddfaf977 100644 --- a/media/libcubeb/src/cubeb-internal.h +++ b/media/libcubeb/src/cubeb-internal.h @@ -4,8 +4,8 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -#if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5) -#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 +#ifndef CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 +#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 #include "cubeb/cubeb.h" diff --git a/media/libcubeb/src/cubeb.c b/media/libcubeb/src/cubeb.c index a4b6a5329a1..fface813247 100644 --- a/media/libcubeb/src/cubeb.c +++ b/media/libcubeb/src/cubeb.c @@ -5,7 +5,7 @@ * accompanying file LICENSE for details. */ #include -#if defined(HAVE_CONFIG_H) +#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "cubeb/cubeb.h" @@ -21,33 +21,30 @@ struct cubeb_stream { struct cubeb * context; }; -#if defined(USE_PULSE) +#ifdef USE_PULSE int pulse_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_ALSA) +#ifdef USE_ALSA int alsa_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_AUDIOQUEUE) +#ifdef USE_AUDIOQUEUE int audioqueue_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_AUDIOUNIT) +#ifdef USE_AUDIOUNIT int audiounit_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_DIRECTSOUND) +#ifdef USE_DIRECTSOUND int directsound_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_WINMM) +#ifdef USE_WINMM int winmm_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_SNDIO) +#ifdef USE_SNDIO int sndio_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_OPENSL) +#ifdef USE_OPENSL int opensl_init(cubeb ** context, char const * context_name); #endif -#if defined(USE_AUDIOTRACK) -int audiotrack_init(cubeb ** context, char const * context_name); -#endif int validate_stream_params(cubeb_stream_params stream_params) @@ -81,32 +78,29 @@ int cubeb_init(cubeb ** context, char const * context_name) { int (* init[])(cubeb **, char const *) = { -#if defined(USE_PULSE) +#ifdef USE_PULSE pulse_init, #endif -#if defined(USE_ALSA) +#ifdef USE_ALSA alsa_init, #endif -#if defined(USE_AUDIOUNIT) +#ifdef USE_AUDIOUNIT audiounit_init, #endif -#if defined(USE_AUDIOQUEUE) +#ifdef USE_AUDIOQUEUE audioqueue_init, #endif -#if defined(USE_WINMM) +#ifdef USE_WINMM winmm_init, #endif -#if defined(USE_DIRECTSOUND) +#ifdef USE_DIRECTSOUND directsound_init, #endif -#if defined(USE_SNDIO) +#ifdef USE_SNDIO sndio_init, #endif -#if defined(USE_OPENSL) +#ifdef USE_OPENSL opensl_init, -#endif -#if defined(USE_AUDIOTRACK) - audiotrack_init, #endif }; int i; diff --git a/media/libcubeb/src/cubeb_audiotrack.c b/media/libcubeb/src/cubeb_audiotrack.c deleted file mode 100644 index 723dc947e37..00000000000 --- a/media/libcubeb/src/cubeb_audiotrack.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright © 2013 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#define NDEBUG -#include -#include -#include -#include -#include -#include "android/log.h" - -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#include "android/audiotrack_definitions.h" - -#ifndef ALOG -#if defined(DEBUG) || defined(FORCE_ALOG) -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args) -#else -#define ALOG(args...) -#endif -#endif - -/** - * A lot of bytes for safety. It should be possible to bring this down a bit. */ -#define SIZE_AUDIOTRACK_INSTANCE 256 - -/** - * call dlsym to get the symbol |mangled_name|, handle the error and store the - * pointer in |pointer|. Because depending on Android version, we want different - * symbols, not finding a symbol is not an error. */ -#define DLSYM_DLERROR(mangled_name, pointer, lib) \ - do { \ - pointer = dlsym(lib, mangled_name); \ - if (!pointer) { \ - ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \ - } else { \ - ALOG("%stm: OK", mangled_name); \ - } \ - } while(0); - -static struct cubeb_ops const audiotrack_ops; -void audiotrack_destroy(cubeb * context); -void audiotrack_stream_destroy(cubeb_stream * stream); - -struct AudioTrack { - /* only available on ICS and later. */ - /* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate); - /* if this symbol is not availble, and the next one is, we know - * we are on a Froyo (Android 2.2) device. */ - void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int); - void* (*ctor_froyo)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int); - void* (*dtor)(void* instance); - void (*start)(void* instance); - void (*pause)(void* instance); - uint32_t (*latency)(void* instance); - status_t (*check)(void* instance); - status_t (*get_position)(void* instance, uint32_t* position); - /* only used on froyo. */ - /* static */ int (*get_output_frame_count)(int* frame_count, int stream); - /* static */ int (*get_output_latency)(uint32_t* frame_count, int stream); - /* static */ int (*get_output_samplingrate)(int* frame_count, int stream); - status_t (*set_marker_position)(void* instance, unsigned int); - -}; - -struct cubeb { - struct cubeb_ops const * ops; - void * library; - struct AudioTrack klass; -}; - -struct cubeb_stream { - cubeb * context; - cubeb_stream_params params; - cubeb_data_callback data_callback; - cubeb_state_callback state_callback; - void * instance; - void * user_ptr; - /* Number of frames that have been passed to the AudioTrack callback */ - long unsigned written; - int draining; -}; - -static void -audiotrack_refill(int event, void* user, void* info) -{ - cubeb_stream * stream = user; - switch (event) { - case EVENT_MORE_DATA: { - long got = 0; - struct Buffer * b = (struct Buffer*)info; - - if (stream->draining) { - return; - } - - got = stream->data_callback(stream, stream->user_ptr, b->raw, b->frameCount); - - stream->written += got; - - if (got != (long)b->frameCount) { - uint32_t p; - stream->draining = 1; - /* set a marker so we are notified when the are done draining, that is, - * when every frame has been played by android. */ - stream->context->klass.set_marker_position(stream->instance, stream->written); - } - - break; - } - case EVENT_UNDERRUN: - ALOG("underrun in cubeb backend."); - break; - case EVENT_LOOP_END: - assert(0 && "We don't support the loop feature of audiotrack."); - break; - case EVENT_MARKER: - assert(stream->draining); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); - break; - case EVENT_NEW_POS: - assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack."); - break; - case EVENT_BUFFER_END: - assert(0 && "Should not happen."); - break; - } -} - -/* We are running on froyo if we found the right AudioTrack constructor */ -static int -audiotrack_version_is_froyo(cubeb * ctx) -{ - return ctx->klass.ctor_froyo != NULL; -} - -int -audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count) -{ - status_t status; - /* Recent Android have a getMinFrameCount method. On Froyo, we have to compute it by hand. */ - if (audiotrack_version_is_froyo(ctx)) { - int samplerate, frame_count, latency, min_buffer_count; - status = ctx->klass.get_output_frame_count(&frame_count, AUDIO_STREAM_TYPE_MUSIC); - if (status) { - ALOG("error getting the output frame count."); - return CUBEB_ERROR; - } - status = ctx->klass.get_output_latency((uint32_t*)&latency, AUDIO_STREAM_TYPE_MUSIC); - if (status) { - ALOG("error getting the output frame count."); - return CUBEB_ERROR; - } - status = ctx->klass.get_output_samplingrate(&samplerate, AUDIO_STREAM_TYPE_MUSIC); - if (status) { - ALOG("error getting the output frame count."); - return CUBEB_ERROR; - } - - /* Those numbers were found reading the Android source. It is the minimum - * numbers that will be accepted by the AudioTrack class, hence yielding the - * best latency possible. - * See https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/media/libmedia/AudioTrack.cpp - * around line 181 for Android 2.2 */ - min_buffer_count = latency / ((1000 * frame_count) / samplerate); - min_buffer_count = min_buffer_count < 2 ? min_buffer_count : 2; - *min_frame_count = (frame_count * params->rate * min_buffer_count) / samplerate; - return CUBEB_OK; - } - /* Recent Android have a getMinFrameCount method. */ - status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate); - if (status != 0) { - ALOG("error getting the min frame count"); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -int -audiotrack_init(cubeb ** context, char const * context_name) -{ - cubeb * ctx; - struct AudioTrack* c; - - assert(context); - *context = NULL; - - ctx = calloc(1, sizeof(*ctx)); - assert(ctx); - - /* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android - * 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on - * the first call to a dlsym'ed function. Somehow this does not happen when - * using only the name of the library. */ - ctx->library = dlopen("libmedia.so", RTLD_LAZY); - if (!ctx->library) { - ALOG("dlopen error: %s.", dlerror()); - free(ctx); - return CUBEB_ERROR; - } - - /* Recent Android first, then Froyo. */ - DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library); - if (!ctx->klass.ctor) { - DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i", ctx->klass.ctor_froyo, ctx->library); - assert(ctx->klass.ctor_froyo); - } - DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library); - - DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library); - DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library); - - /* |getMinFrameCount| is not available on Froyo. */ - if (audiotrack_version_is_froyo(ctx)) { - DLSYM_DLERROR("_ZN7android11AudioSystem19getOutputFrameCountEPii", ctx->klass.get_output_frame_count, ctx->library); - DLSYM_DLERROR("_ZN7android11AudioSystem16getOutputLatencyEPji", ctx->klass.get_output_latency, ctx->library); - DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library); - } else { - DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); - } - - DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library); - - /* check that we have a combination of symbol that makes sense */ - c = &ctx->klass; - if(!((c->ctor || c->ctor_froyo) && /* at least on ctor. */ - c->dtor && c->latency && c->check && - /* at least one way to get the minimum frame count to request. */ - ((c->get_output_frame_count && c->get_output_latency && c->get_output_samplingrate) || - c->get_min_frame_count) && - c->start && c->pause && c->get_position && c->set_marker_position)) { - ALOG("Could not find all the symbols we need."); - audiotrack_destroy(ctx); - return CUBEB_ERROR; - } - - ctx->ops = &audiotrack_ops; - - *context = ctx; - - return CUBEB_OK; -} - -char const * -audiotrack_get_backend_id(cubeb * context) -{ - return "audiotrack"; -} - -void -audiotrack_destroy(cubeb * context) -{ - assert(context); - - dlclose(context->library); - - free(context); -} - -int -audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, - cubeb_stream_params stream_params, unsigned int latency, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) -{ - struct cubeb_stream * stm; - int32_t channels; - int32_t min_frame_count; - - assert(ctx && stream); - - if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE || - stream_params.format == CUBEB_SAMPLE_FLOAT32BE) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - if (audiotrack_get_min_frame_count(ctx, &stream_params, &min_frame_count)) { - return CUBEB_ERROR; - } - - stm = calloc(1, sizeof(*stm)); - assert(stm); - - stm->context = ctx; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->params = stream_params; - - stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1); - (*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad; - assert(stm->instance && "cubeb: EOM"); - - if (audiotrack_version_is_froyo(ctx)) { - channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Froyo : AUDIO_CHANNEL_OUT_MONO_Froyo; - } else { - channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS; - } - - if (audiotrack_version_is_froyo(ctx)) { - ctx->klass.ctor_froyo(stm->instance, - AUDIO_STREAM_TYPE_MUSIC, - stm->params.rate, - AUDIO_FORMAT_PCM_16_BIT, - channels, - min_frame_count, - 0, - audiotrack_refill, - stm, - 0); - } else { - ctx->klass.ctor(stm->instance, - AUDIO_STREAM_TYPE_MUSIC, - stm->params.rate, - AUDIO_FORMAT_PCM_16_BIT, - channels, - min_frame_count, - 0, - audiotrack_refill, - stm, - 0, - 0); - } - - assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad); - - if (ctx->klass.check(stm->instance)) { - ALOG("stream not initialized properly."); - audiotrack_stream_destroy(stm); - return CUBEB_ERROR; - } - - *stream = stm; - - return CUBEB_OK; -} - -void -audiotrack_stream_destroy(cubeb_stream * stream) -{ - assert(stream->context); - - stream->context->klass.dtor(stream->instance); - - free(stream->instance); - stream->instance = NULL; - free(stream); -} - -int -audiotrack_stream_start(cubeb_stream * stream) -{ - assert(stream->instance); - - stream->context->klass.start(stream->instance); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); - - return CUBEB_OK; -} - -int -audiotrack_stream_stop(cubeb_stream * stream) -{ - assert(stream->instance); - - stream->context->klass.pause(stream->instance); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); - - return CUBEB_OK; -} - -int -audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position) -{ - uint32_t p; - - assert(stream->instance && position); - stream->context->klass.get_position(stream->instance, &p); - *position = p; - - return CUBEB_OK; -} - -static struct cubeb_ops const audiotrack_ops = { - .init = audiotrack_init, - .get_backend_id = audiotrack_get_backend_id, - .destroy = audiotrack_destroy, - .stream_init = audiotrack_stream_init, - .stream_destroy = audiotrack_stream_destroy, - .stream_start = audiotrack_stream_start, - .stream_stop = audiotrack_stream_stop, - .stream_get_position = audiotrack_stream_get_position -}; diff --git a/media/libcubeb/src/cubeb_opensl.c b/media/libcubeb/src/cubeb_opensl.c index 27c6a61a8c1..ffeca02f3b6 100644 --- a/media/libcubeb/src/cubeb_opensl.c +++ b/media/libcubeb/src/cubeb_opensl.c @@ -10,7 +10,6 @@ #include #include #if defined(__ANDROID__) -#include "android/sles_definitions.h" #include #endif #include "cubeb/cubeb.h" @@ -124,8 +123,6 @@ opensl_init(cubeb ** context, char const * context_name) ctx = calloc(1, sizeof(*ctx)); assert(ctx); - ctx->ops = &opensl_ops; - ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY); if (!ctx->lib) { free(ctx); diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c index c817f06ff6f..d39290e94dc 100644 --- a/media/libcubeb/src/cubeb_sndio.c +++ b/media/libcubeb/src/cubeb_sndio.c @@ -12,7 +12,7 @@ #include "cubeb/cubeb.h" #include "cubeb-internal.h" -#if defined(CUBEB_SNDIO_DEBUG) +#ifdef CUBEB_SNDIO_DEBUG #define DPR(...) fprintf(stderr, __VA_ARGS__); #else #define DPR(...) do {} while(0) @@ -217,7 +217,7 @@ sndio_stream_init(cubeb *context, DPR("sndio_stream_init() unsupported params\n"); return CUBEB_ERROR_INVALID_FORMAT; } - sio_onmove(s->hdl, sndio_onmove, s); + sio_onmove(s->hdl, cubeb_onmove, s); s->active = 0; s->nfr = rpar.round; s->bpf = rpar.bps * rpar.pchan; @@ -262,7 +262,7 @@ sndio_stream_start(cubeb_stream *s) DPR("sndio_stream_start()\n"); s->active = 1; - err = pthread_create(&s->th, NULL, sndio_mainloop, s); + err = pthread_create(&s->th, NULL, cubeb_mainloop, s); if (err) { s->active = 0; return CUBEB_ERROR; diff --git a/media/libcubeb/src/cubeb_winmm.c b/media/libcubeb/src/cubeb_winmm.c index e8b20bfb854..bd4e03ac6c6 100644 --- a/media/libcubeb/src/cubeb_winmm.c +++ b/media/libcubeb/src/cubeb_winmm.c @@ -19,7 +19,7 @@ #include "cubeb-internal.h" /* This is missing from the MinGW headers. Use a safe fallback. */ -#if !defined(MEMORY_ALLOCATION_ALIGNMENT) +#ifndef MEMORY_ALLOCATION_ALIGNMENT #define MEMORY_ALLOCATION_ALIGNMENT 16 #endif diff --git a/media/libcubeb/update.sh b/media/libcubeb/update.sh index f3cfc0e3d07..403c4f13a04 100644 --- a/media/libcubeb/update.sh +++ b/media/libcubeb/update.sh @@ -10,9 +10,6 @@ cp $1/src/cubeb_audiounit.c src cp $1/src/cubeb_pulse.c src cp $1/src/cubeb_sndio.c src cp $1/src/cubeb_opensl.c src -cp $1/src/cubeb_audiotrack.c src -cp $1/src/android/audiotrack_definitions.h src/android -cp $1/src/android/sles_definitions.h src/android cp $1/LICENSE . cp $1/README . cp $1/AUTHORS . diff --git a/mobile/android/config/mozconfigs/android-armv6/debug b/mobile/android/config/mozconfigs/android-armv6/debug index 1881e203d64..b167e8469d8 100644 --- a/mobile/android/config/mozconfigs/android-armv6/debug +++ b/mobile/android/config/mozconfigs/android-armv6/debug @@ -12,7 +12,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib # IonMonkey disabled in bug 789373 diff --git a/mobile/android/config/mozconfigs/android-armv6/l10n-nightly b/mobile/android/config/mozconfigs/android-armv6/l10n-nightly index f452220ed72..3ee1d89caec 100644 --- a/mobile/android/config/mozconfigs/android-armv6/l10n-nightly +++ b/mobile/android/config/mozconfigs/android-armv6/l10n-nightly @@ -18,7 +18,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/l10n-release b/mobile/android/config/mozconfigs/android-armv6/l10n-release index 71a141106cc..218e320c138 100644 --- a/mobile/android/config/mozconfigs/android-armv6/l10n-release +++ b/mobile/android/config/mozconfigs/android-armv6/l10n-release @@ -15,7 +15,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/nightly b/mobile/android/config/mozconfigs/android-armv6/nightly index 7ca7e29f82b..91b4d37e7f0 100644 --- a/mobile/android/config/mozconfigs/android-armv6/nightly +++ b/mobile/android/config/mozconfigs/android-armv6/nightly @@ -9,7 +9,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/release b/mobile/android/config/mozconfigs/android-armv6/release index 902b10707a8..0587d54478b 100644 --- a/mobile/android/config/mozconfigs/android-armv6/release +++ b/mobile/android/config/mozconfigs/android-armv6/release @@ -9,7 +9,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-noion/nightly b/mobile/android/config/mozconfigs/android-noion/nightly index 97529ad095d..c12bf30f690 100644 --- a/mobile/android/config/mozconfigs/android-noion/nightly +++ b/mobile/android/config/mozconfigs/android-noion/nightly @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/debug b/mobile/android/config/mozconfigs/android/debug index 188c56852d2..29593209d9c 100644 --- a/mobile/android/config/mozconfigs/android/debug +++ b/mobile/android/config/mozconfigs/android/debug @@ -11,7 +11,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib export JAVA_HOME=/tools/jdk6 diff --git a/mobile/android/config/mozconfigs/android/l10n-nightly b/mobile/android/config/mozconfigs/android/l10n-nightly index 8971688bc03..12b2722c5d1 100644 --- a/mobile/android/config/mozconfigs/android/l10n-nightly +++ b/mobile/android/config/mozconfigs/android/l10n-nightly @@ -17,7 +17,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/l10n-release b/mobile/android/config/mozconfigs/android/l10n-release index cf37d2b6ffe..18ac37ab1db 100644 --- a/mobile/android/config/mozconfigs/android/l10n-release +++ b/mobile/android/config/mozconfigs/android/l10n-release @@ -14,7 +14,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/nightly b/mobile/android/config/mozconfigs/android/nightly index 95cd28160a3..adced2ff9e9 100644 --- a/mobile/android/config/mozconfigs/android/nightly +++ b/mobile/android/config/mozconfigs/android/nightly @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/release b/mobile/android/config/mozconfigs/android/release index 8b11fe2ff46..6533b7f908d 100644 --- a/mobile/android/config/mozconfigs/android/release +++ b/mobile/android/config/mozconfigs/android/release @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=9 +ac_add_options --with-android-version=5 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} From cd524b325b56bce6902c78f98c637d2a1365bf4d Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 14 Mar 2013 21:22:28 -0700 Subject: [PATCH 011/202] Back out 298a598dde74 (bug 849988) for test_bug_779982.html timeouts CLOSED TREE --- dom/apps/src/AppsServiceChild.jsm | 10 +++++++--- dom/apps/src/AppsUtils.jsm | 25 ------------------------- dom/apps/src/Makefile.in | 2 +- dom/apps/src/Webapps.jsm | 13 +++++++++---- 4 files changed, 17 insertions(+), 33 deletions(-) diff --git a/dom/apps/src/AppsServiceChild.jsm b/dom/apps/src/AppsServiceChild.jsm index 06682ab279c..a4ad71e053d 100644 --- a/dom/apps/src/AppsServiceChild.jsm +++ b/dom/apps/src/AppsServiceChild.jsm @@ -103,7 +103,6 @@ this.DOMApplicationRegistry = { debug("getAppFromObserverMessage " + aMessage); return AppsUtils.getAppFromObserverMessage(this.webapps. aMessage); }, - getCoreAppsBasePath: function getCoreAppsBasePath() { debug("getCoreAppsBasePath() not yet supported on child!"); return null; @@ -115,8 +114,13 @@ this.DOMApplicationRegistry = { }, getAppInfo: function getAppInfo(aAppId) { - return AppsUtils.getAppInfo(this.webapps, aAppId); - } + if (!this.webapps[aAppId]) { + debug("No webapp for " + aAppId); + return null; + } + return { "basePath": this.webapps[aAppId].basePath + "/", + "isCoreApp": !this.webapps[aAppId].removable }; + }, } DOMApplicationRegistry.init(); diff --git a/dom/apps/src/AppsUtils.jsm b/dom/apps/src/AppsUtils.jsm index cf4ad908395..01a4e30082c 100644 --- a/dom/apps/src/AppsUtils.jsm +++ b/dom/apps/src/AppsUtils.jsm @@ -11,7 +11,6 @@ const Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { return Cc["@mozilla.org/network/util;1"] @@ -177,30 +176,6 @@ this.AppsUtils = { return null; }, - getCoreAppsBasePath: function getCoreAppsBasePath() { - debug("getCoreAppsBasePath()"); - return FileUtils.getDir("coreAppsDir", ["webapps"], false).path; - }, - - getAppInfo: function getAppInfo(aApps, aAppId) { - if (!aApps[aAppId]) { - debug("No webapp for " + aAppId); - return null; - } - - // We can have 3rd party apps that are non-removable, - // so we can't use the 'removable' property for isCoreApp - // Instead, we check if the app is installed under /system/b2g - let isCoreApp = false; - let app = aApps[aAppId]; -#ifdef MOZ_WIDGET_GONK - isCoreApp = app.basePath == this.getCoreAppsBasePath(); -#endif - debug(app.name + " isCoreApp: " + isCoreApp); - return { "basePath": app.basePath + "/", - "isCoreApp": isCoreApp }; - }, - /** * From https://developer.mozilla.org/en/OpenWebApps/The_Manifest * Only the name property is mandatory. diff --git a/dom/apps/src/Makefile.in b/dom/apps/src/Makefile.in index 873ec1d0169..ce88eedeefc 100644 --- a/dom/apps/src/Makefile.in +++ b/dom/apps/src/Makefile.in @@ -21,11 +21,11 @@ EXTRA_PP_COMPONENTS = \ EXTRA_PP_JS_MODULES += \ Webapps.jsm \ - AppsUtils.jsm \ $(NULL) EXTRA_JS_MODULES += \ AppsServiceChild.jsm \ + AppsUtils.jsm \ OfflineCacheInstaller.jsm \ PermissionsInstaller.jsm \ PermissionsTable.jsm \ diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index b3db26d51e3..a57591e80ea 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -283,6 +283,7 @@ this.DOMApplicationRegistry = { } app.origin = "app://" + aId; + app.removable = true; // Extract the manifest.webapp file from application.zip. let zipFile = baseDir.clone(); @@ -860,7 +861,12 @@ this.DOMApplicationRegistry = { }, getAppInfo: function getAppInfo(aAppId) { - return AppsUtils.getAppInfo(this.webapps, aAppId); + if (!this.webapps[aAppId]) { + debug("No webapp for " + aAppId); + return null; + } + return { "basePath": this.webapps[aAppId].basePath + "/", + "isCoreApp": !this.webapps[aAppId].removable }; }, // Some messages can be listened by several content processes: @@ -2021,8 +2027,7 @@ this.DOMApplicationRegistry = { } // the manifest file used to be named manifest.json, so fallback on this. - let baseDir = this.webapps[id].basePath == this.getCoreAppsBasePath() - ? "coreAppsDir" : DIRECTORY_NAME; + let baseDir = (this.webapps[id].removable ? DIRECTORY_NAME : "coreAppsDir"); let file = FileUtils.getFile(baseDir, ["webapps", id, "manifest.webapp"], true); if (!file.exists()) { file = FileUtils.getFile(baseDir, ["webapps", id, "update.webapp"], true); @@ -2656,7 +2661,7 @@ this.DOMApplicationRegistry = { }, getCoreAppsBasePath: function() { - return AppsUtils.getCoreAppsBasePath(); + return FileUtils.getDir("coreAppsDir", ["webapps"], false).path; }, getWebAppsBasePath: function getWebAppsBasePath() { From cc246bf97f8fa437a0b93cb30762a1b6eaf372dc Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 14 Mar 2013 21:28:10 -0700 Subject: [PATCH 012/202] Bug 837011 - CallCompiler should return 'true' because it doesn't throw an exception (r=dvander) --HG-- extra : rebase_source : 326ad9707d64db437d0a7dddfef9bbffbb59038e --- js/src/methodjit/MonoIC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 045d340e4ec..b07bdeab577 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -811,7 +811,7 @@ class CallCompiler : public BaseCompiler if (!linker.verifyRange(f.chunk())) { disable(); - return false; + return true; } linker.link(noIonCode, ic.icCall()); From 6e22400cdafc2dd1f1a6342e3d6c9649cbb6f2df Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 15 Mar 2013 17:41:50 +1300 Subject: [PATCH 013/202] Bug 702504 - Make USE_WIDGET_LAYERS disabled a test failure. r=roc --- layout/tools/reftest/reftest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index 440ce5c13bd..d178803e180 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -1353,7 +1353,7 @@ function DoDrawWindow(ctx, x, y, w, h) } else { // Output a special warning because we need to be able to detect // this whenever it happens. - gDumpLog("REFTEST INFO | WARNING: USE_WIDGET_LAYERS disabled\n"); + gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | WARNING: USE_WIDGET_LAYERS disabled\n"); } gDumpLog("REFTEST INFO | drawWindow flags = " + flagsStr + "; window size = " + gContainingWindow.innerWidth + "," + gContainingWindow.innerHeight + From 51ef2b4b8697947b376a2ab25d8f002a179f9611 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 15 Mar 2013 17:41:52 +1300 Subject: [PATCH 014/202] Bug 676746 - Forcibly initialize the persistant layer manager when we try to read its backend type. r=joe --- dom/base/nsDOMWindowUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index fbd752c703b..e0201a961fb 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2192,7 +2192,7 @@ nsDOMWindowUtils::GetLayerManagerType(nsAString& aType) if (!widget) return NS_ERROR_FAILURE; - LayerManager *mgr = widget->GetLayerManager(); + LayerManager *mgr = widget->GetLayerManager(nsIWidget::LAYER_MANAGER_PERSISTENT); if (!mgr) return NS_ERROR_FAILURE; From fd7156900ee07942743cb50662bfcf9cb5514512 Mon Sep 17 00:00:00 2001 From: Wan-Teh Chang Date: Thu, 14 Mar 2013 21:53:25 -0700 Subject: [PATCH 015/202] Bug 841651: Update NSPR to NSPR_4_9_6_RTM. Remove the private patch. --- nsprpub/TAG-INFO | 2 +- nsprpub/config/prdepend.h | 1 - nsprpub/patches/README | 5 - nsprpub/patches/linux-setpriority.patch | 278 ------------------------ nsprpub/pr/include/prinit.h | 4 +- 5 files changed, 3 insertions(+), 287 deletions(-) delete mode 100644 nsprpub/patches/README delete mode 100644 nsprpub/patches/linux-setpriority.patch diff --git a/nsprpub/TAG-INFO b/nsprpub/TAG-INFO index 24a7586fd96..d26f36a53c5 100644 --- a/nsprpub/TAG-INFO +++ b/nsprpub/TAG-INFO @@ -1 +1 @@ -NSPR_4_9_6_BETA2 +NSPR_4_9_6_RTM diff --git a/nsprpub/config/prdepend.h b/nsprpub/config/prdepend.h index 6c66b37ca0f..e49e92677e3 100644 --- a/nsprpub/config/prdepend.h +++ b/nsprpub/config/prdepend.h @@ -10,4 +10,3 @@ */ #error "Do not include this header file." - diff --git a/nsprpub/patches/README b/nsprpub/patches/README deleted file mode 100644 index 3167a732e20..00000000000 --- a/nsprpub/patches/README +++ /dev/null @@ -1,5 +0,0 @@ -This directory contains patches that were added locally -on top of the NSPR release. - -* linux-setpriority.patch: Bug 841651 - Implement PR_SetThreadPriority() - on Linux-based platforms using per-thread nice values. diff --git a/nsprpub/patches/linux-setpriority.patch b/nsprpub/patches/linux-setpriority.patch deleted file mode 100644 index 2c0091ef41e..00000000000 --- a/nsprpub/patches/linux-setpriority.patch +++ /dev/null @@ -1,278 +0,0 @@ -diff --git a/nsprpub/configure.in b/nsprpub/configure.in ---- a/nsprpub/configure.in -+++ b/nsprpub/configure.in -@@ -2583,17 +2583,17 @@ dnl AC_HEADER_TIME - dnl AC_STRUCT_TM - - dnl ======================================================== - dnl Checks for library functions. - dnl ======================================================== - AC_PROG_GCC_TRADITIONAL - _SAVE_LIBS="$LIBS" - LIBS="$LIBS $OS_LIBS" --AC_CHECK_FUNCS(lchown strerror dladdr) -+AC_CHECK_FUNCS(dladdr gettid lchown setpriority strerror syscall) - LIBS="$_SAVE_LIBS" - - dnl AC_FUNC_MEMCMP - dnl AC_FUNC_MMAP - dnl AC_FUNC_SETVBUF_REVERSED - dnl AC_FUNC_STRCOLL - dnl AC_FUNC_STRFTIME - dnl AC_FUNC_UTIME_NULL -diff --git a/nsprpub/pr/include/private/primpl.h b/nsprpub/pr/include/private/primpl.h ---- a/nsprpub/pr/include/private/primpl.h -+++ b/nsprpub/pr/include/private/primpl.h -@@ -45,16 +45,20 @@ typedef struct PRSegment PRSegment; - #include "obsolete/probslet.h" - - #ifdef _PR_HAVE_POSIX_SEMAPHORES - #include - #elif defined(_PR_HAVE_SYSV_SEMAPHORES) - #include - #endif - -+#ifdef HAVE_SYSCALL -+#include -+#endif -+ - /************************************************************************* - ***** A Word about Model Dependent Function Naming Convention *********** - *************************************************************************/ - - /* - NSPR 2.0 must implement its function across a range of platforms - including: MAC, Windows/16, Windows/95, Windows/NT, and several - variants of Unix. Each implementation shares common code as well -@@ -181,16 +185,27 @@ typedef struct PTDebug - PRUintn cvars_created, cvars_destroyed; - PRUintn cvars_notified, delayed_cv_deletes; - } PTDebug; - - #endif /* defined(DEBUG) */ - - NSPR_API(void) PT_FPrintStats(PRFileDesc *fd, const char *msg); - -+/* -+ * On Linux and its derivatives POSIX priority scheduling works only for -+ * real-time threads. On those platforms we set thread's nice values -+ * instead which requires us to track kernel thread IDs for each POSIX -+ * thread we create. -+ */ -+#if defined(LINUX) && defined(HAVE_SETPRIORITY) && \ -+ ((defined(HAVE_SYSCALL) && defined(SYS_gettid)) || defined(HAVE_GETTID)) -+#define _PR_NICE_PRIORITY_SCHEDULING -+#endif -+ - #else /* defined(_PR_PTHREADS) */ - - NSPR_API(void) PT_FPrintStats(PRFileDesc *fd, const char *msg); - - /* - ** This section is contains those parts needed to implement NSPR on - ** platforms in general. One would assume that the pthreads implementation - ** included lots of the same types, at least conceptually. -@@ -1535,16 +1550,19 @@ struct PRThread { - PRInt32 osErrorCode; /* mapping of errorCode | zero */ - PRIntn errorStringLength; /* textLength from last call to PR_SetErrorText() */ - PRInt32 errorStringSize; /* malloc()'d size of buffer | zero */ - char *errorString; /* current error string | NULL */ - char *name; /* thread's name */ - - #if defined(_PR_PTHREADS) - pthread_t id; /* pthread identifier for the thread */ -+#ifdef _PR_NICE_PRIORITY_SCHEDULING -+ pid_t tid; /* Linux-specific kernel thread ID */ -+#endif - PRBool okToDelete; /* ok to delete the PRThread struct? */ - PRCondVar *waiting; /* where the thread is waiting | NULL */ - void *sp; /* recorded sp for garbage collection */ - PRThread *next, *prev; /* simple linked list of all threads */ - PRUint32 suspend; /* used to store suspend and resume flags */ - #ifdef PT_NO_SIGTIMEDWAIT - pthread_mutex_t suspendResumeMutex; - pthread_cond_t suspendResumeCV; -diff --git a/nsprpub/pr/src/pthreads/ptthread.c b/nsprpub/pr/src/pthreads/ptthread.c ---- a/nsprpub/pr/src/pthreads/ptthread.c -+++ b/nsprpub/pr/src/pthreads/ptthread.c -@@ -23,16 +23,24 @@ - - #ifdef SYMBIAN - /* In Open C sched_get_priority_min/max do not work properly, so we undefine - * _POSIX_THREAD_PRIORITY_SCHEDULING here. - */ - #undef _POSIX_THREAD_PRIORITY_SCHEDULING - #endif - -+#ifdef _PR_NICE_PRIORITY_SCHEDULING -+#undef _POSIX_THREAD_PRIORITY_SCHEDULING -+#include -+#ifndef HAVE_GETTID -+#define gettid() (syscall(SYS_gettid)) -+#endif -+#endif -+ - /* - * Record whether or not we have the privilege to set the scheduling - * policy and priority of threads. 0 means that privilege is available. - * EPERM means that privilege is not available. - */ - - static PRIntn pt_schedpriv = 0; - extern PRLock *_pr_sleeplock; -@@ -49,26 +57,35 @@ static struct _PT_Bookeeping - PRInt32 minPrio, maxPrio; /* range of scheduling priorities */ - #endif - } pt_book = {0}; - - static void _pt_thread_death(void *arg); - static void _pt_thread_death_internal(void *arg, PRBool callDestructors); - static void init_pthread_gc_support(void); - --#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING) -+#if defined(_PR_DCETHREADS) || \ -+ defined(_POSIX_THREAD_PRIORITY_SCHEDULING) || \ -+ defined(_PR_NICE_PRIORITY_SCHEDULING) - static PRIntn pt_PriorityMap(PRThreadPriority pri) - { - #ifdef NTO - /* This priority algorithm causes lots of problems on Neutrino - * for now I have just hard coded everything to run at priority 10 - * until I can come up with a new algorithm. - * Jerry.Kirk@Nexwarecorp.com - */ - return 10; -+#elif defined(_PR_NICE_PRIORITY_SCHEDULING) -+ /* This maps high priorities to low nice values: -+ * PR_PRIORITY_LOW 1 -+ * PR_PRIORITY_NORMAL 0 -+ * PR_PRIORITY_HIGH -1 -+ * PR_PRIORITY_URGENT -2 */ -+ return 1 - pri; - #else - return pt_book.minPrio + - pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST; - #endif - } - #endif - - /* -@@ -93,28 +110,46 @@ static void _PR_InitializeStack(PRThread - } - } - - static void *_pt_root(void *arg) - { - PRIntn rv; - PRThread *thred = (PRThread*)arg; - PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE; -+#ifdef _PR_NICE_PRIORITY_SCHEDULING -+ pid_t tid; -+#endif - - /* - * Both the parent thread and this new thread set thred->id. - * The new thread must ensure that thred->id is set before - * it executes its startFunc. The parent thread must ensure - * that thred->id is set before PR_CreateThread() returns. - * Both threads set thred->id without holding a lock. Since - * they are writing the same value, this unprotected double - * write should be safe. - */ - thred->id = pthread_self(); - -+#ifdef _PR_NICE_PRIORITY_SCHEDULING -+ /* -+ * We need to know the kernel thread ID of each thread in order to -+ * set its priority hence we do it here instead of at creation time. -+ */ -+ tid = gettid(); -+ -+ rv = setpriority(PRIO_PROCESS, tid, pt_PriorityMap(thred->priority)); -+ -+ PR_Lock(pt_book.ml); -+ thred->tid = tid; -+ PR_NotifyAllCondVar(pt_book.cv); -+ PR_Unlock(pt_book.ml); -+#endif -+ - /* - ** DCE Threads can't detach during creation, so do it late. - ** I would like to do it only here, but that doesn't seem - ** to work. - */ - #if defined(_PR_DCETHREADS) - if (detached) - { -@@ -219,16 +254,19 @@ static PRThread* pt_AttachThread(void) - /* PR_NEWZAP must not call PR_GetCurrentThread() */ - thred = PR_NEWZAP(PRThread); - if (NULL != thred) - { - int rv; - - thred->priority = PR_PRIORITY_NORMAL; - thred->id = pthread_self(); -+#ifdef _PR_NICE_PRIORITY_SCHEDULING -+ thred->tid = gettid(); -+#endif - rv = pthread_setspecific(pt_book.key, thred); - PR_ASSERT(0 == rv); - - thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN; - PR_Lock(pt_book.ml); - - /* then put it into the list */ - thred->prev = pt_book.last; -@@ -639,16 +677,31 @@ PR_IMPLEMENT(void) PR_SetThreadPriority( - pt_schedpriv = EPERM; - PR_LOG(_pr_thread_lm, PR_LOG_MIN, - ("PR_SetThreadPriority: no thread scheduling privilege")); - } - } - if (rv != 0) - rv = -1; - } -+#elif defined(_PR_NICE_PRIORITY_SCHEDULING) -+ PR_Lock(pt_book.ml); -+ while (thred->tid == 0) -+ PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); -+ PR_Unlock(pt_book.ml); -+ -+ rv = setpriority(PRIO_PROCESS, thred->tid, pt_PriorityMap(newPri)); -+ -+ if (rv == -1 && errno == EPERM) -+ { -+ /* We don't set pt_schedpriv to EPERM because adjusting the nice -+ * value might be permitted for certain ranges but not others */ -+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, -+ ("PR_SetThreadPriority: no thread scheduling privilege")); -+ } - #endif - - thred->priority = newPri; - } /* PR_SetThreadPriority */ - - PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred) - { - /* -@@ -857,16 +910,19 @@ void _PR_InitThreads( - pt_book.cv = PR_NewCondVar(pt_book.ml); - PR_ASSERT(NULL != pt_book.cv); - thred = PR_NEWZAP(PRThread); - PR_ASSERT(NULL != thred); - thred->arg = NULL; - thred->startFunc = NULL; - thred->priority = priority; - thred->id = pthread_self(); -+#ifdef _PR_NICE_PRIORITY_SCHEDULING -+ thred->tid = gettid(); -+#endif - - thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD); - if (PR_SYSTEM_THREAD == type) - { - thred->state |= PT_THREAD_SYSTEM; - pt_book.system += 1; - pt_book.this_many = 0; - } diff --git a/nsprpub/pr/include/prinit.h b/nsprpub/pr/include/prinit.h index 9e5da3502fd..5506bd04a1d 100644 --- a/nsprpub/pr/include/prinit.h +++ b/nsprpub/pr/include/prinit.h @@ -31,11 +31,11 @@ PR_BEGIN_EXTERN_C ** The format of the version string is ** ".[.] []" */ -#define PR_VERSION "4.9.6 Beta" +#define PR_VERSION "4.9.6" #define PR_VMAJOR 4 #define PR_VMINOR 9 #define PR_VPATCH 6 -#define PR_BETA PR_TRUE +#define PR_BETA PR_FALSE /* ** PRVersionCheck From e4f9d837cb27b8f2507a39797e56c10ea7946081 Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Thu, 14 Mar 2013 00:21:34 -0400 Subject: [PATCH 016/202] bug 850973 - remove nsAccUtils::IsText() r=surkov --- accessible/src/base/nsAccUtils.cpp | 4 ++-- accessible/src/base/nsAccUtils.h | 10 ---------- accessible/src/generic/Accessible.cpp | 2 +- accessible/src/generic/HyperTextAccessible.cpp | 14 ++++++-------- .../tests/mochitest/text/test_multiline.html | 2 +- .../tests/mochitest/text/test_singleline.html | 4 ++-- .../tests/mochitest/text/test_whitespaces.html | 4 ++-- 7 files changed, 14 insertions(+), 26 deletions(-) diff --git a/accessible/src/base/nsAccUtils.cpp b/accessible/src/base/nsAccUtils.cpp index 49c03da53f1..28906439e79 100644 --- a/accessible/src/base/nsAccUtils.cpp +++ b/accessible/src/base/nsAccUtils.cpp @@ -409,7 +409,7 @@ nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible) uint32_t childCount = aAccessible->ChildCount(); for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { Accessible* child = aAccessible->GetChildAt(childIdx); - if (IsText(child)) { + if (!IsEmbeddedObject(child)) { foundText = true; break; } @@ -429,7 +429,7 @@ nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible) uint32_t nsAccUtils::TextLength(Accessible* aAccessible) { - if (!IsText(aAccessible)) + if (IsEmbeddedObject(aAccessible)) return 1; TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf(); diff --git a/accessible/src/base/nsAccUtils.h b/accessible/src/base/nsAccUtils.h index fcdfa69a616..1422e748a6f 100644 --- a/accessible/src/base/nsAccUtils.h +++ b/accessible/src/base/nsAccUtils.h @@ -232,16 +232,6 @@ public: static bool IsTextInterfaceSupportCorrect(Accessible* aAccessible); #endif - /** - * Return true if the given accessible has text role. - */ - static bool IsText(nsIAccessible *aAcc) - { - uint32_t role = Role(aAcc); - return role == nsIAccessibleRole::ROLE_TEXT_LEAF || - role == nsIAccessibleRole::ROLE_STATICTEXT; - } - /** * Return text length of the given accessible, return 0 on failure. */ diff --git a/accessible/src/generic/Accessible.cpp b/accessible/src/generic/Accessible.cpp index af36deee570..d0554855c22 100644 --- a/accessible/src/generic/Accessible.cpp +++ b/accessible/src/generic/Accessible.cpp @@ -2671,7 +2671,7 @@ Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild) mChildren[idx]->mIndexInParent = idx; } - if (nsAccUtils::IsText(aChild)) + if (!nsAccUtils::IsEmbeddedObject(aChild)) SetChildrenFlag(eMixedChildren); mEmbeddedObjCollector = nullptr; diff --git a/accessible/src/generic/HyperTextAccessible.cpp b/accessible/src/generic/HyperTextAccessible.cpp index fbebe08fdd4..9e01b5c5fb0 100644 --- a/accessible/src/generic/HyperTextAccessible.cpp +++ b/accessible/src/generic/HyperTextAccessible.cpp @@ -266,7 +266,7 @@ HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset, } nsIFrame *primaryFrame = frame; endFrame = frame; - if (nsAccUtils::IsText(childAcc)) { + if (!nsAccUtils::IsEmbeddedObject(childAcc)) { // We only need info up to rendered offset -- that is what we're // converting to content offset int32_t substringEndOffset = -1; @@ -708,14 +708,12 @@ HyperTextAccessible::GetRelativeOffset(nsIPresShell* aPresShell, nsresult rv; int32_t contentOffset = aFromOffset; - if (nsAccUtils::IsText(aFromAccessible)) { - nsIFrame *frame = aFromAccessible->GetFrame(); - NS_ENSURE_TRUE(frame, -1); + nsIFrame *frame = aFromAccessible->GetFrame(); + NS_ENSURE_TRUE(frame, -1); - if (frame->GetType() == nsGkAtoms::textFrame) { - rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset); - NS_ENSURE_SUCCESS(rv, -1); - } + if (frame->GetType() == nsGkAtoms::textFrame) { + rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset); + NS_ENSURE_SUCCESS(rv, -1); } nsPeekOffsetStruct pos(aAmount, aDirection, contentOffset, diff --git a/accessible/tests/mochitest/text/test_multiline.html b/accessible/tests/mochitest/text/test_multiline.html index 06fde8ae620..5137ac66ec9 100644 --- a/accessible/tests/mochitest/text/test_multiline.html +++ b/accessible/tests/mochitest/text/test_multiline.html @@ -16,7 +16,7 @@ function doTest() { - SimpleTest.expectAssertions(60); + SimpleTest.expectAssertions(52); // __o__n__e__w__o__r__d__\n // 0 1 2 3 4 5 6 7 diff --git a/accessible/tests/mochitest/text/test_singleline.html b/accessible/tests/mochitest/text/test_singleline.html index 0b2c134e2f5..cfd6a0dcf35 100644 --- a/accessible/tests/mochitest/text/test_singleline.html +++ b/accessible/tests/mochitest/text/test_singleline.html @@ -12,9 +12,9 @@ src="../text.js"> diff --git a/layout/base/tests/chrome/test_printpreview_bug396024.xul b/layout/base/tests/chrome/test_printpreview_bug396024.xul index 651f494b126..eb086d35a63 100644 --- a/layout/base/tests/chrome/test_printpreview_bug396024.xul +++ b/layout/base/tests/chrome/test_printpreview_bug396024.xul @@ -16,28 +16,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=396024 diff --git a/layout/base/tests/chrome/test_printpreview_bug482976.xul b/layout/base/tests/chrome/test_printpreview_bug482976.xul index ff3181c62a2..52918d5ba0b 100644 --- a/layout/base/tests/chrome/test_printpreview_bug482976.xul +++ b/layout/base/tests/chrome/test_printpreview_bug482976.xul @@ -16,28 +16,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=482976 diff --git a/layout/forms/test/test_bug536567_perwindowpb.html b/layout/forms/test/test_bug536567_perwindowpb.html index 61e2b04f908..fc59071a551 100644 --- a/layout/forms/test/test_bug536567_perwindowpb.html +++ b/layout/forms/test/test_bug536567_perwindowpb.html @@ -14,11 +14,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=536567

 
-  
-
-
-
-
-
- - diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl index 736b6755ba9..a1b40a61e2e 100644 --- a/dom/webidl/AudioContext.webidl +++ b/dom/webidl/AudioContext.webidl @@ -18,7 +18,6 @@ interface AudioContext { readonly attribute AudioDestinationNode destination; readonly attribute float sampleRate; - readonly attribute double currentTime; readonly attribute AudioListener listener; [Creator, Throws] From cd51bc591a9a0811f44d442762604833e50c24fa Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Fri, 15 Mar 2013 16:28:07 +1300 Subject: [PATCH 023/202] Bug 850713 - Bump the required NDK version to 9. r=blassey.bugs,mh+mozilla --- CLOBBER | 2 +- build/autoconf/android.m4 | 12 ++---------- configure.in | 2 +- js/src/build/autoconf/android.m4 | 12 ++---------- mobile/android/config/mozconfigs/android-armv6/debug | 2 +- .../config/mozconfigs/android-armv6/l10n-nightly | 2 +- .../config/mozconfigs/android-armv6/l10n-release | 2 +- .../android/config/mozconfigs/android-armv6/nightly | 2 +- .../android/config/mozconfigs/android-armv6/release | 2 +- .../android/config/mozconfigs/android-noion/nightly | 2 +- mobile/android/config/mozconfigs/android/debug | 2 +- .../android/config/mozconfigs/android/l10n-nightly | 2 +- .../android/config/mozconfigs/android/l10n-release | 2 +- mobile/android/config/mozconfigs/android/nightly | 2 +- mobile/android/config/mozconfigs/android/release | 2 +- 15 files changed, 17 insertions(+), 33 deletions(-) diff --git a/CLOBBER b/CLOBBER index 528eafeabf3..0f4a591320f 100644 --- a/CLOBBER +++ b/CLOBBER @@ -15,4 +15,4 @@ # # Note: The description below will be part of the error message shown to users. # -Bug 847890 appears to need a clobber +Bug 850713 requires a clobber diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 index f7a82272ae9..aa18b4dd565 100644 --- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -26,19 +26,11 @@ MOZ_ARG_ENABLE_BOOL(android-libstdcxx, MOZ_ANDROID_LIBSTDCXX=1, MOZ_ANDROID_LIBSTDCXX= ) -dnl default android_version is different per target cpu -case "$target_cpu" in -arm) - android_version=5 - ;; -i?86|mipsel) - android_version=9 - ;; -esac +android_version=9 MOZ_ARG_WITH_STRING(android-version, [ --with-android-version=VER - android platform version, default 5 for arm, 9 for x86/mips], + android platform version, default 9], android_version=$withval) MOZ_ARG_WITH_STRING(android-platform, diff --git a/configure.in b/configure.in index e3ebd1eaa02..e13437fd574 100644 --- a/configure.in +++ b/configure.in @@ -9268,7 +9268,7 @@ if test -n "$_WRAP_MALLOC"; then fi if test -z "$MOZ_NATIVE_NSPR"; then - ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla" + ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla --with-android-version=$ANDROID_VERSION" if test -z "$MOZ_DEBUG"; then ac_configure_args="$ac_configure_args --disable-debug" else diff --git a/js/src/build/autoconf/android.m4 b/js/src/build/autoconf/android.m4 index f7a82272ae9..aa18b4dd565 100644 --- a/js/src/build/autoconf/android.m4 +++ b/js/src/build/autoconf/android.m4 @@ -26,19 +26,11 @@ MOZ_ARG_ENABLE_BOOL(android-libstdcxx, MOZ_ANDROID_LIBSTDCXX=1, MOZ_ANDROID_LIBSTDCXX= ) -dnl default android_version is different per target cpu -case "$target_cpu" in -arm) - android_version=5 - ;; -i?86|mipsel) - android_version=9 - ;; -esac +android_version=9 MOZ_ARG_WITH_STRING(android-version, [ --with-android-version=VER - android platform version, default 5 for arm, 9 for x86/mips], + android platform version, default 9], android_version=$withval) MOZ_ARG_WITH_STRING(android-platform, diff --git a/mobile/android/config/mozconfigs/android-armv6/debug b/mobile/android/config/mozconfigs/android-armv6/debug index b167e8469d8..1881e203d64 100644 --- a/mobile/android/config/mozconfigs/android-armv6/debug +++ b/mobile/android/config/mozconfigs/android-armv6/debug @@ -12,7 +12,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib # IonMonkey disabled in bug 789373 diff --git a/mobile/android/config/mozconfigs/android-armv6/l10n-nightly b/mobile/android/config/mozconfigs/android-armv6/l10n-nightly index 3ee1d89caec..f452220ed72 100644 --- a/mobile/android/config/mozconfigs/android-armv6/l10n-nightly +++ b/mobile/android/config/mozconfigs/android-armv6/l10n-nightly @@ -18,7 +18,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/l10n-release b/mobile/android/config/mozconfigs/android-armv6/l10n-release index 218e320c138..71a141106cc 100644 --- a/mobile/android/config/mozconfigs/android-armv6/l10n-release +++ b/mobile/android/config/mozconfigs/android-armv6/l10n-release @@ -15,7 +15,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/nightly b/mobile/android/config/mozconfigs/android-armv6/nightly index 91b4d37e7f0..7ca7e29f82b 100644 --- a/mobile/android/config/mozconfigs/android-armv6/nightly +++ b/mobile/android/config/mozconfigs/android-armv6/nightly @@ -9,7 +9,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-armv6/release b/mobile/android/config/mozconfigs/android-armv6/release index 0587d54478b..902b10707a8 100644 --- a/mobile/android/config/mozconfigs/android-armv6/release +++ b/mobile/android/config/mozconfigs/android-armv6/release @@ -9,7 +9,7 @@ ac_add_options --with-arch=armv6 ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android-noion/nightly b/mobile/android/config/mozconfigs/android-noion/nightly index c12bf30f690..97529ad095d 100644 --- a/mobile/android/config/mozconfigs/android-noion/nightly +++ b/mobile/android/config/mozconfigs/android-noion/nightly @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/debug b/mobile/android/config/mozconfigs/android/debug index 29593209d9c..188c56852d2 100644 --- a/mobile/android/config/mozconfigs/android/debug +++ b/mobile/android/config/mozconfigs/android/debug @@ -11,7 +11,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib export JAVA_HOME=/tools/jdk6 diff --git a/mobile/android/config/mozconfigs/android/l10n-nightly b/mobile/android/config/mozconfigs/android/l10n-nightly index 12b2722c5d1..8971688bc03 100644 --- a/mobile/android/config/mozconfigs/android/l10n-nightly +++ b/mobile/android/config/mozconfigs/android/l10n-nightly @@ -17,7 +17,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/l10n-release b/mobile/android/config/mozconfigs/android/l10n-release index 18ac37ab1db..cf37d2b6ffe 100644 --- a/mobile/android/config/mozconfigs/android/l10n-release +++ b/mobile/android/config/mozconfigs/android/l10n-release @@ -14,7 +14,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/nightly b/mobile/android/config/mozconfigs/android/nightly index adced2ff9e9..95cd28160a3 100644 --- a/mobile/android/config/mozconfigs/android/nightly +++ b/mobile/android/config/mozconfigs/android/nightly @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/mobile/android/config/mozconfigs/android/release b/mobile/android/config/mozconfigs/android/release index 6533b7f908d..8b11fe2ff46 100644 --- a/mobile/android/config/mozconfigs/android/release +++ b/mobile/android/config/mozconfigs/android/release @@ -8,7 +8,7 @@ ac_add_options --target=arm-linux-androideabi ac_add_options --with-android-ndk="/tools/android-ndk-r8c" ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16" ac_add_options --with-android-gnu-compiler-version=4.6 -ac_add_options --with-android-version=5 +ac_add_options --with-android-version=9 ac_add_options --with-system-zlib ac_add_options --enable-updater ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} From 008f7914b8374965352fcc77447a6e4d94522954 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 13 Mar 2013 19:36:46 +0100 Subject: [PATCH 024/202] Bug 698328 - Add a new cubeb backend based on AudioTrack.cpp. r=kinetik --- configure.in | 5 +- media/libcubeb/AUTHORS | 1 + media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/include/cubeb.h | 8 +- media/libcubeb/src/Makefile.in | 9 +- .../src/android/audiotrack_definitions.h | 81 ++++ media/libcubeb/src/android/sles_definitions.h | 67 +++ media/libcubeb/src/audiotrack_definitions.h | 81 ++++ media/libcubeb/src/cubeb-internal.h | 4 +- media/libcubeb/src/cubeb.c | 40 +- media/libcubeb/src/cubeb_audiotrack.c | 402 ++++++++++++++++++ media/libcubeb/src/cubeb_opensl.c | 3 + media/libcubeb/src/cubeb_sndio.c | 2 +- media/libcubeb/src/cubeb_winmm.c | 2 +- media/libcubeb/update.sh | 3 + 15 files changed, 677 insertions(+), 33 deletions(-) create mode 100644 media/libcubeb/src/android/audiotrack_definitions.h create mode 100644 media/libcubeb/src/android/sles_definitions.h create mode 100644 media/libcubeb/src/audiotrack_definitions.h create mode 100644 media/libcubeb/src/cubeb_audiotrack.c diff --git a/configure.in b/configure.in index e13437fd574..21c264afee1 100644 --- a/configure.in +++ b/configure.in @@ -5657,10 +5657,7 @@ fi if test -n "$MOZ_CUBEB"; then case "$target" in *-android*|*-linuxandroid*) - if test -n "$gonkdir"; then - AC_DEFINE(MOZ_CUBEB) - fi - dnl No Android implementation of libcubeb yet. + AC_DEFINE(MOZ_CUBEB) ;; *-linux*) AC_DEFINE(MOZ_CUBEB) diff --git a/media/libcubeb/AUTHORS b/media/libcubeb/AUTHORS index 74d9c7d8127..d98205cb4d9 100644 --- a/media/libcubeb/AUTHORS +++ b/media/libcubeb/AUTHORS @@ -1,3 +1,4 @@ Matthew Gregan Alexandre Ratchov Michael Wu +Paul Adenot diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index 7232df15862..bed89182d1d 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was f2d524c34b6f75d85053034c3075c2ff08383769. +The git commit ID used was 0c7d97523096a7d4ae363974393d06f77c4592c9. diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h index 4494a6eb304..bc398e0d1b9 100644 --- a/media/libcubeb/include/cubeb.h +++ b/media/libcubeb/include/cubeb.h @@ -4,12 +4,12 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -#ifndef CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 -#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 +#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382) +#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 #include -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { #endif @@ -226,7 +226,7 @@ int cubeb_stream_stop(cubeb_stream * stream); @retval CUBEB_ERROR */ int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position); -#ifdef __cplusplus +#if defined(__cplusplus) } #endif diff --git a/media/libcubeb/src/Makefile.in b/media/libcubeb/src/Makefile.in index 5b06a460026..9d6d2366220 100644 --- a/media/libcubeb/src/Makefile.in +++ b/media/libcubeb/src/Makefile.in @@ -28,14 +28,17 @@ DEFINES += -DUSE_WINMM endif ifeq ($(OS_TARGET),Android) -ifeq ($(MOZ_WIDGET_TOOLKIT),gonk) +ifneq ($(MOZ_WIDGET_TOOLKIT),gonk) +CSRCS += \ + cubeb_audiotrack.c \ + $(NULL) +DEFINES += -DUSE_AUDIOTRACK +endif CSRCS += \ cubeb_opensl.c \ $(NULL) DEFINES += -DUSE_OPENSL endif -# No Android implementation of libcubeb yet. -endif ifeq ($(OS_TARGET),Darwin) CSRCS += \ diff --git a/media/libcubeb/src/android/audiotrack_definitions.h b/media/libcubeb/src/android/audiotrack_definitions.h new file mode 100644 index 00000000000..da3032d4084 --- /dev/null +++ b/media/libcubeb/src/android/audiotrack_definitions.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* + * The following definitions are copied from the android sources. Only the + * relevant enum member and values needed are copied. + */ + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h + */ +typedef int32_t status_t; + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h + */ +struct Buffer { + uint32_t flags; + int channelCount; + int format; + size_t frameCount; + size_t size; + union { + void* raw; + short* i16; + int8_t* i8; + }; +}; + +enum event_type { + EVENT_MORE_DATA = 0, + EVENT_UNDERRUN = 1, + EVENT_LOOP_END = 2, + EVENT_MARKER = 3, + EVENT_NEW_POS = 4, + EVENT_BUFFER_END = 5 +}; + +/** + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h + * and + * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h + */ + +#define AUDIO_STREAM_TYPE_MUSIC 3 + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2, + AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, + AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) +} AudioTrack_ChannelMapping_ICS; + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo = 0x4, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo = 0x8, + AUDIO_CHANNEL_OUT_MONO_Froyo = AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo, + AUDIO_CHANNEL_OUT_STEREO_Froyo = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo) +} AudioTrack_ChannelMapping_Froyo; + +typedef enum { + AUDIO_FORMAT_PCM = 0x00000000, + AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, + AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), +} AudioTrack_SampleType; + diff --git a/media/libcubeb/src/android/sles_definitions.h b/media/libcubeb/src/android/sles_definitions.h new file mode 100644 index 00000000000..f5670c131c2 --- /dev/null +++ b/media/libcubeb/src/android/sles_definitions.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file is similar to the file "OpenSLES_AndroidConfiguration.h" found in + * the Android NDK, but removes the #ifdef __cplusplus defines, so we can keep + * using a C compiler in cubeb. + */ + +#ifndef OPENSL_ES_ANDROIDCONFIGURATION_H_ +#define OPENSL_ES_ANDROIDCONFIGURATION_H_ + +/*---------------------------------------------------------------------------*/ +/* Android AudioRecorder configuration */ +/*---------------------------------------------------------------------------*/ + +/** Audio recording preset */ +/** Audio recording preset key */ +#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset") +/** Audio recording preset values */ +/** preset "none" cannot be set, it is used to indicate the current settings + * do not match any of the presets. */ +#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32) 0x00000000) +/** generic recording configuration on the platform */ +#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32) 0x00000001) +/** uses the microphone audio source with the same orientation as the camera + * if available, the main device microphone otherwise */ +#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32) 0x00000002) +/** uses the main microphone tuned for voice recognition */ +#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32) 0x00000003) + +/*---------------------------------------------------------------------------*/ +/* Android AudioPlayer configuration */ +/*---------------------------------------------------------------------------*/ + +/** Audio playback stream type */ +/** Audio playback stream type key */ +#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType") + +/** Audio playback stream type values */ +/* same as android.media.AudioManager.STREAM_VOICE_CALL */ +#define SL_ANDROID_STREAM_VOICE ((SLint32) 0x00000000) +/* same as android.media.AudioManager.STREAM_SYSTEM */ +#define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001) +/* same as android.media.AudioManager.STREAM_RING */ +#define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002) +/* same as android.media.AudioManager.STREAM_MUSIC */ +#define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003) +/* same as android.media.AudioManager.STREAM_ALARM */ +#define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004) +/* same as android.media.AudioManager.STREAM_NOTIFICATION */ +#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005) + +#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */ diff --git a/media/libcubeb/src/audiotrack_definitions.h b/media/libcubeb/src/audiotrack_definitions.h new file mode 100644 index 00000000000..da3032d4084 --- /dev/null +++ b/media/libcubeb/src/audiotrack_definitions.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* + * The following definitions are copied from the android sources. Only the + * relevant enum member and values needed are copied. + */ + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h + */ +typedef int32_t status_t; + +/* + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h + */ +struct Buffer { + uint32_t flags; + int channelCount; + int format; + size_t frameCount; + size_t size; + union { + void* raw; + short* i16; + int8_t* i8; + }; +}; + +enum event_type { + EVENT_MORE_DATA = 0, + EVENT_UNDERRUN = 1, + EVENT_LOOP_END = 2, + EVENT_MARKER = 3, + EVENT_NEW_POS = 4, + EVENT_BUFFER_END = 5 +}; + +/** + * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h + * and + * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h + */ + +#define AUDIO_STREAM_TYPE_MUSIC 3 + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2, + AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, + AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) +} AudioTrack_ChannelMapping_ICS; + +enum { + AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo = 0x4, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo = 0x8, + AUDIO_CHANNEL_OUT_MONO_Froyo = AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo, + AUDIO_CHANNEL_OUT_STEREO_Froyo = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo) +} AudioTrack_ChannelMapping_Froyo; + +typedef enum { + AUDIO_FORMAT_PCM = 0x00000000, + AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, + AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), +} AudioTrack_SampleType; + diff --git a/media/libcubeb/src/cubeb-internal.h b/media/libcubeb/src/cubeb-internal.h index ec2ddfaf977..f617a56adf0 100644 --- a/media/libcubeb/src/cubeb-internal.h +++ b/media/libcubeb/src/cubeb-internal.h @@ -4,8 +4,8 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -#ifndef CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 -#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 +#if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5) +#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 #include "cubeb/cubeb.h" diff --git a/media/libcubeb/src/cubeb.c b/media/libcubeb/src/cubeb.c index fface813247..a4b6a5329a1 100644 --- a/media/libcubeb/src/cubeb.c +++ b/media/libcubeb/src/cubeb.c @@ -5,7 +5,7 @@ * accompanying file LICENSE for details. */ #include -#ifdef HAVE_CONFIG_H +#if defined(HAVE_CONFIG_H) #include "config.h" #endif #include "cubeb/cubeb.h" @@ -21,30 +21,33 @@ struct cubeb_stream { struct cubeb * context; }; -#ifdef USE_PULSE +#if defined(USE_PULSE) int pulse_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_ALSA +#if defined(USE_ALSA) int alsa_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_AUDIOQUEUE +#if defined(USE_AUDIOQUEUE) int audioqueue_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_AUDIOUNIT +#if defined(USE_AUDIOUNIT) int audiounit_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_DIRECTSOUND +#if defined(USE_DIRECTSOUND) int directsound_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_WINMM +#if defined(USE_WINMM) int winmm_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_SNDIO +#if defined(USE_SNDIO) int sndio_init(cubeb ** context, char const * context_name); #endif -#ifdef USE_OPENSL +#if defined(USE_OPENSL) int opensl_init(cubeb ** context, char const * context_name); #endif +#if defined(USE_AUDIOTRACK) +int audiotrack_init(cubeb ** context, char const * context_name); +#endif int validate_stream_params(cubeb_stream_params stream_params) @@ -78,29 +81,32 @@ int cubeb_init(cubeb ** context, char const * context_name) { int (* init[])(cubeb **, char const *) = { -#ifdef USE_PULSE +#if defined(USE_PULSE) pulse_init, #endif -#ifdef USE_ALSA +#if defined(USE_ALSA) alsa_init, #endif -#ifdef USE_AUDIOUNIT +#if defined(USE_AUDIOUNIT) audiounit_init, #endif -#ifdef USE_AUDIOQUEUE +#if defined(USE_AUDIOQUEUE) audioqueue_init, #endif -#ifdef USE_WINMM +#if defined(USE_WINMM) winmm_init, #endif -#ifdef USE_DIRECTSOUND +#if defined(USE_DIRECTSOUND) directsound_init, #endif -#ifdef USE_SNDIO +#if defined(USE_SNDIO) sndio_init, #endif -#ifdef USE_OPENSL +#if defined(USE_OPENSL) opensl_init, +#endif +#if defined(USE_AUDIOTRACK) + audiotrack_init, #endif }; int i; diff --git a/media/libcubeb/src/cubeb_audiotrack.c b/media/libcubeb/src/cubeb_audiotrack.c new file mode 100644 index 00000000000..723dc947e37 --- /dev/null +++ b/media/libcubeb/src/cubeb_audiotrack.c @@ -0,0 +1,402 @@ +/* + * Copyright © 2013 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ + +#define NDEBUG +#include +#include +#include +#include +#include +#include "android/log.h" + +#include "cubeb/cubeb.h" +#include "cubeb-internal.h" +#include "android/audiotrack_definitions.h" + +#ifndef ALOG +#if defined(DEBUG) || defined(FORCE_ALOG) +#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args) +#else +#define ALOG(args...) +#endif +#endif + +/** + * A lot of bytes for safety. It should be possible to bring this down a bit. */ +#define SIZE_AUDIOTRACK_INSTANCE 256 + +/** + * call dlsym to get the symbol |mangled_name|, handle the error and store the + * pointer in |pointer|. Because depending on Android version, we want different + * symbols, not finding a symbol is not an error. */ +#define DLSYM_DLERROR(mangled_name, pointer, lib) \ + do { \ + pointer = dlsym(lib, mangled_name); \ + if (!pointer) { \ + ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \ + } else { \ + ALOG("%stm: OK", mangled_name); \ + } \ + } while(0); + +static struct cubeb_ops const audiotrack_ops; +void audiotrack_destroy(cubeb * context); +void audiotrack_stream_destroy(cubeb_stream * stream); + +struct AudioTrack { + /* only available on ICS and later. */ + /* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate); + /* if this symbol is not availble, and the next one is, we know + * we are on a Froyo (Android 2.2) device. */ + void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int); + void* (*ctor_froyo)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int); + void* (*dtor)(void* instance); + void (*start)(void* instance); + void (*pause)(void* instance); + uint32_t (*latency)(void* instance); + status_t (*check)(void* instance); + status_t (*get_position)(void* instance, uint32_t* position); + /* only used on froyo. */ + /* static */ int (*get_output_frame_count)(int* frame_count, int stream); + /* static */ int (*get_output_latency)(uint32_t* frame_count, int stream); + /* static */ int (*get_output_samplingrate)(int* frame_count, int stream); + status_t (*set_marker_position)(void* instance, unsigned int); + +}; + +struct cubeb { + struct cubeb_ops const * ops; + void * library; + struct AudioTrack klass; +}; + +struct cubeb_stream { + cubeb * context; + cubeb_stream_params params; + cubeb_data_callback data_callback; + cubeb_state_callback state_callback; + void * instance; + void * user_ptr; + /* Number of frames that have been passed to the AudioTrack callback */ + long unsigned written; + int draining; +}; + +static void +audiotrack_refill(int event, void* user, void* info) +{ + cubeb_stream * stream = user; + switch (event) { + case EVENT_MORE_DATA: { + long got = 0; + struct Buffer * b = (struct Buffer*)info; + + if (stream->draining) { + return; + } + + got = stream->data_callback(stream, stream->user_ptr, b->raw, b->frameCount); + + stream->written += got; + + if (got != (long)b->frameCount) { + uint32_t p; + stream->draining = 1; + /* set a marker so we are notified when the are done draining, that is, + * when every frame has been played by android. */ + stream->context->klass.set_marker_position(stream->instance, stream->written); + } + + break; + } + case EVENT_UNDERRUN: + ALOG("underrun in cubeb backend."); + break; + case EVENT_LOOP_END: + assert(0 && "We don't support the loop feature of audiotrack."); + break; + case EVENT_MARKER: + assert(stream->draining); + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); + break; + case EVENT_NEW_POS: + assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack."); + break; + case EVENT_BUFFER_END: + assert(0 && "Should not happen."); + break; + } +} + +/* We are running on froyo if we found the right AudioTrack constructor */ +static int +audiotrack_version_is_froyo(cubeb * ctx) +{ + return ctx->klass.ctor_froyo != NULL; +} + +int +audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count) +{ + status_t status; + /* Recent Android have a getMinFrameCount method. On Froyo, we have to compute it by hand. */ + if (audiotrack_version_is_froyo(ctx)) { + int samplerate, frame_count, latency, min_buffer_count; + status = ctx->klass.get_output_frame_count(&frame_count, AUDIO_STREAM_TYPE_MUSIC); + if (status) { + ALOG("error getting the output frame count."); + return CUBEB_ERROR; + } + status = ctx->klass.get_output_latency((uint32_t*)&latency, AUDIO_STREAM_TYPE_MUSIC); + if (status) { + ALOG("error getting the output frame count."); + return CUBEB_ERROR; + } + status = ctx->klass.get_output_samplingrate(&samplerate, AUDIO_STREAM_TYPE_MUSIC); + if (status) { + ALOG("error getting the output frame count."); + return CUBEB_ERROR; + } + + /* Those numbers were found reading the Android source. It is the minimum + * numbers that will be accepted by the AudioTrack class, hence yielding the + * best latency possible. + * See https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/media/libmedia/AudioTrack.cpp + * around line 181 for Android 2.2 */ + min_buffer_count = latency / ((1000 * frame_count) / samplerate); + min_buffer_count = min_buffer_count < 2 ? min_buffer_count : 2; + *min_frame_count = (frame_count * params->rate * min_buffer_count) / samplerate; + return CUBEB_OK; + } + /* Recent Android have a getMinFrameCount method. */ + status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate); + if (status != 0) { + ALOG("error getting the min frame count"); + return CUBEB_ERROR; + } + return CUBEB_OK; +} + +int +audiotrack_init(cubeb ** context, char const * context_name) +{ + cubeb * ctx; + struct AudioTrack* c; + + assert(context); + *context = NULL; + + ctx = calloc(1, sizeof(*ctx)); + assert(ctx); + + /* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android + * 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on + * the first call to a dlsym'ed function. Somehow this does not happen when + * using only the name of the library. */ + ctx->library = dlopen("libmedia.so", RTLD_LAZY); + if (!ctx->library) { + ALOG("dlopen error: %s.", dlerror()); + free(ctx); + return CUBEB_ERROR; + } + + /* Recent Android first, then Froyo. */ + DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library); + if (!ctx->klass.ctor) { + DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i", ctx->klass.ctor_froyo, ctx->library); + assert(ctx->klass.ctor_froyo); + } + DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library); + + DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library); + DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library); + + /* |getMinFrameCount| is not available on Froyo. */ + if (audiotrack_version_is_froyo(ctx)) { + DLSYM_DLERROR("_ZN7android11AudioSystem19getOutputFrameCountEPii", ctx->klass.get_output_frame_count, ctx->library); + DLSYM_DLERROR("_ZN7android11AudioSystem16getOutputLatencyEPji", ctx->klass.get_output_latency, ctx->library); + DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library); + } else { + DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); + } + + DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library); + + /* check that we have a combination of symbol that makes sense */ + c = &ctx->klass; + if(!((c->ctor || c->ctor_froyo) && /* at least on ctor. */ + c->dtor && c->latency && c->check && + /* at least one way to get the minimum frame count to request. */ + ((c->get_output_frame_count && c->get_output_latency && c->get_output_samplingrate) || + c->get_min_frame_count) && + c->start && c->pause && c->get_position && c->set_marker_position)) { + ALOG("Could not find all the symbols we need."); + audiotrack_destroy(ctx); + return CUBEB_ERROR; + } + + ctx->ops = &audiotrack_ops; + + *context = ctx; + + return CUBEB_OK; +} + +char const * +audiotrack_get_backend_id(cubeb * context) +{ + return "audiotrack"; +} + +void +audiotrack_destroy(cubeb * context) +{ + assert(context); + + dlclose(context->library); + + free(context); +} + +int +audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, + cubeb_stream_params stream_params, unsigned int latency, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, + void * user_ptr) +{ + struct cubeb_stream * stm; + int32_t channels; + int32_t min_frame_count; + + assert(ctx && stream); + + if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE || + stream_params.format == CUBEB_SAMPLE_FLOAT32BE) { + return CUBEB_ERROR_INVALID_FORMAT; + } + + if (audiotrack_get_min_frame_count(ctx, &stream_params, &min_frame_count)) { + return CUBEB_ERROR; + } + + stm = calloc(1, sizeof(*stm)); + assert(stm); + + stm->context = ctx; + stm->data_callback = data_callback; + stm->state_callback = state_callback; + stm->user_ptr = user_ptr; + stm->params = stream_params; + + stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1); + (*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad; + assert(stm->instance && "cubeb: EOM"); + + if (audiotrack_version_is_froyo(ctx)) { + channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Froyo : AUDIO_CHANNEL_OUT_MONO_Froyo; + } else { + channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS; + } + + if (audiotrack_version_is_froyo(ctx)) { + ctx->klass.ctor_froyo(stm->instance, + AUDIO_STREAM_TYPE_MUSIC, + stm->params.rate, + AUDIO_FORMAT_PCM_16_BIT, + channels, + min_frame_count, + 0, + audiotrack_refill, + stm, + 0); + } else { + ctx->klass.ctor(stm->instance, + AUDIO_STREAM_TYPE_MUSIC, + stm->params.rate, + AUDIO_FORMAT_PCM_16_BIT, + channels, + min_frame_count, + 0, + audiotrack_refill, + stm, + 0, + 0); + } + + assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad); + + if (ctx->klass.check(stm->instance)) { + ALOG("stream not initialized properly."); + audiotrack_stream_destroy(stm); + return CUBEB_ERROR; + } + + *stream = stm; + + return CUBEB_OK; +} + +void +audiotrack_stream_destroy(cubeb_stream * stream) +{ + assert(stream->context); + + stream->context->klass.dtor(stream->instance); + + free(stream->instance); + stream->instance = NULL; + free(stream); +} + +int +audiotrack_stream_start(cubeb_stream * stream) +{ + assert(stream->instance); + + stream->context->klass.start(stream->instance); + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); + + return CUBEB_OK; +} + +int +audiotrack_stream_stop(cubeb_stream * stream) +{ + assert(stream->instance); + + stream->context->klass.pause(stream->instance); + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); + + return CUBEB_OK; +} + +int +audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position) +{ + uint32_t p; + + assert(stream->instance && position); + stream->context->klass.get_position(stream->instance, &p); + *position = p; + + return CUBEB_OK; +} + +static struct cubeb_ops const audiotrack_ops = { + .init = audiotrack_init, + .get_backend_id = audiotrack_get_backend_id, + .destroy = audiotrack_destroy, + .stream_init = audiotrack_stream_init, + .stream_destroy = audiotrack_stream_destroy, + .stream_start = audiotrack_stream_start, + .stream_stop = audiotrack_stream_stop, + .stream_get_position = audiotrack_stream_get_position +}; diff --git a/media/libcubeb/src/cubeb_opensl.c b/media/libcubeb/src/cubeb_opensl.c index ffeca02f3b6..27c6a61a8c1 100644 --- a/media/libcubeb/src/cubeb_opensl.c +++ b/media/libcubeb/src/cubeb_opensl.c @@ -10,6 +10,7 @@ #include #include #if defined(__ANDROID__) +#include "android/sles_definitions.h" #include #endif #include "cubeb/cubeb.h" @@ -123,6 +124,8 @@ opensl_init(cubeb ** context, char const * context_name) ctx = calloc(1, sizeof(*ctx)); assert(ctx); + ctx->ops = &opensl_ops; + ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY); if (!ctx->lib) { free(ctx); diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c index d39290e94dc..49f7deed488 100644 --- a/media/libcubeb/src/cubeb_sndio.c +++ b/media/libcubeb/src/cubeb_sndio.c @@ -12,7 +12,7 @@ #include "cubeb/cubeb.h" #include "cubeb-internal.h" -#ifdef CUBEB_SNDIO_DEBUG +#if defined(CUBEB_SNDIO_DEBUG) #define DPR(...) fprintf(stderr, __VA_ARGS__); #else #define DPR(...) do {} while(0) diff --git a/media/libcubeb/src/cubeb_winmm.c b/media/libcubeb/src/cubeb_winmm.c index bd4e03ac6c6..e8b20bfb854 100644 --- a/media/libcubeb/src/cubeb_winmm.c +++ b/media/libcubeb/src/cubeb_winmm.c @@ -19,7 +19,7 @@ #include "cubeb-internal.h" /* This is missing from the MinGW headers. Use a safe fallback. */ -#ifndef MEMORY_ALLOCATION_ALIGNMENT +#if !defined(MEMORY_ALLOCATION_ALIGNMENT) #define MEMORY_ALLOCATION_ALIGNMENT 16 #endif diff --git a/media/libcubeb/update.sh b/media/libcubeb/update.sh index 403c4f13a04..f3cfc0e3d07 100644 --- a/media/libcubeb/update.sh +++ b/media/libcubeb/update.sh @@ -10,6 +10,9 @@ cp $1/src/cubeb_audiounit.c src cp $1/src/cubeb_pulse.c src cp $1/src/cubeb_sndio.c src cp $1/src/cubeb_opensl.c src +cp $1/src/cubeb_audiotrack.c src +cp $1/src/android/audiotrack_definitions.h src/android +cp $1/src/android/sles_definitions.h src/android cp $1/LICENSE . cp $1/README . cp $1/AUTHORS . From eed08fab4c21f03c8ba0820bb119ce2ae31755f5 Mon Sep 17 00:00:00 2001 From: Landry Breuil Date: Fri, 15 Mar 2013 16:28:07 +1300 Subject: [PATCH 025/202] Bug 851149 - Fix cubeb_sndio build after switchable-backends landing. r=kinetik --- media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/src/cubeb_sndio.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index bed89182d1d..2afd5eefdf0 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was 0c7d97523096a7d4ae363974393d06f77c4592c9. +The git commit ID used was c9c97571980ca77c990a763802c11682a332cbd6. diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c index 49f7deed488..c817f06ff6f 100644 --- a/media/libcubeb/src/cubeb_sndio.c +++ b/media/libcubeb/src/cubeb_sndio.c @@ -217,7 +217,7 @@ sndio_stream_init(cubeb *context, DPR("sndio_stream_init() unsupported params\n"); return CUBEB_ERROR_INVALID_FORMAT; } - sio_onmove(s->hdl, cubeb_onmove, s); + sio_onmove(s->hdl, sndio_onmove, s); s->active = 0; s->nfr = rpar.round; s->bpf = rpar.bps * rpar.pchan; @@ -262,7 +262,7 @@ sndio_stream_start(cubeb_stream *s) DPR("sndio_stream_start()\n"); s->active = 1; - err = pthread_create(&s->th, NULL, cubeb_mainloop, s); + err = pthread_create(&s->th, NULL, sndio_mainloop, s); if (err) { s->active = 0; return CUBEB_ERROR; From 40acbacd40a1d3f1bd835e0cc91cf891db5f4aaa Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 14 Mar 2013 22:33:14 -0700 Subject: [PATCH 026/202] Bug 851169: Add (void)-cast to silence GCC "unused" build warning for variable in Telemetry.cpp whose only usage is currently commented out. r=ehsan --- toolkit/components/telemetry/Telemetry.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 0d241e45827..c80725fc523 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -2095,6 +2095,7 @@ RecordShutdownStartTimeStamp() { // we just keep the last timestamp, the assert is commented for now. static bool recorded = false; // MOZ_ASSERT(!recorded); + (void)recorded; // Silence unused-var warnings (remove when assert re-enabled) recorded = true; #endif From 595f5e39556448f7df70b6ff7cec983b94998cc6 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 14 Mar 2013 22:33:22 -0700 Subject: [PATCH 027/202] Bug 851183: Mark toolkit/components/telemetry/ as FAIL_ON_WARNINGS. r=nfroyd --- toolkit/components/telemetry/Makefile.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/toolkit/components/telemetry/Makefile.in b/toolkit/components/telemetry/Makefile.in index 0c6ca179dcd..e9b237ad42a 100644 --- a/toolkit/components/telemetry/Makefile.in +++ b/toolkit/components/telemetry/Makefile.in @@ -19,6 +19,10 @@ GRE_MODULE = 1 LIBXUL_LIBRARY = 1 EXPORT_LIBRARY = 1 IS_COMPONENT = 1 +ifndef _MSC_VER +# Note: Bug 851306 tracks the MSVC-only build warnings in this directory. +FAIL_ON_WARNINGS = 1 +endif # !_MSC_VER LIBRARY_NAME = telemetry From 4a97aa37203ec4101bb1f8e8feefda8f2ad9e788 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 14 Mar 2013 22:38:26 -0700 Subject: [PATCH 028/202] Bug 850517 - Factor out child window lookup into a helper. r=mrbkap --- dom/base/nsDOMClassInfo.cpp | 13 +------------ dom/base/nsGlobalWindow.cpp | 17 +++++++++++++++++ dom/base/nsGlobalWindow.h | 2 ++ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index a72759fbd9b..02f723fa446 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -5486,18 +5486,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, if (!ObjectIsNativeWrapper(cx, obj) || xpc::WrapperFactory::XrayWrapperNotShadowing(obj, id)) { if (win->GetLength() > 0) { - const jschar *chars = ::JS_GetInternedStringChars(JSID_TO_STRING(id)); - - nsCOMPtr dsn(do_QueryInterface(win->GetDocShell())); - MOZ_ASSERT(dsn); - - nsCOMPtr child; - dsn->FindChildWithName(reinterpret_cast(chars), - false, true, nullptr, nullptr, - getter_AddRefs(child)); - - nsCOMPtr child_win(do_GetInterface(child)); - + nsCOMPtr child_win = win->GetChildWindow(id); if (child_win) { // We found a subframe of the right name, define the property // on the wrapper so that ::NewResolve() doesn't get called diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index edd4e249ec9..71db5dad8e5 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -4627,6 +4627,23 @@ nsGlobalWindow::GetLength(uint32_t* aLength) return NS_OK; } +already_AddRefed +nsGlobalWindow::GetChildWindow(jsid aName) +{ + const jschar *chars = JS_GetInternedStringChars(JSID_TO_STRING(aName)); + + nsCOMPtr dsn(do_QueryInterface(GetDocShell())); + NS_ENSURE_TRUE(dsn, nullptr); + + nsCOMPtr child; + dsn->FindChildWithName(reinterpret_cast(chars), + false, true, nullptr, nullptr, + getter_AddRefs(child)); + + nsCOMPtr child_win(do_GetInterface(child)); + return child_win.forget(); +} + bool nsGlobalWindow::DispatchCustomEvent(const char *aEventName) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 4e7b6c8f215..f722479a05b 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -436,6 +436,8 @@ public: return nullptr; } + already_AddRefed GetChildWindow(jsid aName); + // Returns true if dialogs need to be prevented from appearings for this // window. beingAbused returns whether dialogs are being abused. bool DialogsAreBlocked(bool *aBeingAbused); From 876133f6c7ed39a16d2b7c14fc3268763a455922 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 14 Mar 2013 22:38:26 -0700 Subject: [PATCH 029/202] Bug 850517 - Support named window access via Xray. r=mrbkap --- js/xpconnect/wrappers/XrayWrapper.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index d2f9c99cf37..8b9a8c05cfb 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -175,6 +175,8 @@ private: class XPCWrappedNativeXrayTraits : public XrayTraits { public: + static const XrayType Type = XrayForWrappedNative; + static bool resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id, JSPropertyDescriptor *desc, unsigned flags); virtual bool resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObject *wrapper, @@ -217,6 +219,8 @@ public: class DOMXrayTraits : public XrayTraits { public: + static const XrayType Type = XrayForDOMObject; + static bool resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id, JSPropertyDescriptor *desc, unsigned flags); virtual bool resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObject *wrapper, @@ -1459,6 +1463,29 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } + // We need to handle named access on the Window somewhere other than + // Traits::resolveOwnProperty, because per spec it happens on the Global + // Scope Polluter and thus the resulting properties are non-|own|. However, + // we're set up (below) to cache (on the holder) anything that comes out of + // resolveNativeProperty, which we don't want for something dynamic like + // named access. So we just handle it here. + nsGlobalWindow *win; + if (Traits::Type == XrayForWrappedNative && JSID_IS_STRING(id) && + (win = static_cast(As(wrapper)))) + { + nsCOMPtr childDOMWin = win->GetChildWindow(id); + if (childDOMWin) { + nsGlobalWindow *cwin = static_cast(childDOMWin.get()); + JSObject *childObj = cwin->FastGetGlobalJSObject(); + if (MOZ_UNLIKELY(!childObj)) + return xpc::Throw(cx, NS_ERROR_FAILURE); + mozilla::dom::FillPropertyDescriptor(desc, wrapper, + ObjectValue(*childObj), + /* readOnly = */ true); + return JS_WrapPropertyDescriptor(cx, desc); + } + } + if (!JS_GetPropertyDescriptorById(cx, holder, id, 0, desc)) return false; if (desc->obj) { From 347fa803fc57029e5b746c1920407df859a05ac5 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 14 Mar 2013 22:38:26 -0700 Subject: [PATCH 030/202] Bug 850517 - Switch named children resolution to pure getters on the global scope polluter. r=mrbkap Note that this causes us to throw when assigning to named children, but this is spec-correct. --- dom/base/nsDOMClassInfo.cpp | 86 +++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 02f723fa446..c020b033ceb 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -3648,6 +3648,33 @@ nsWindowSH::GlobalScopePolluterGetProperty(JSContext *cx, JSHandleObject obj, return JS_TRUE; } +// Gets a subframe. +static JSBool +ChildWindowGetter(JSContext *cx, JSHandleObject obj, JSHandleId id, + JSMutableHandleValue vp) +{ + // Grab the native DOM window. + vp.setUndefined(); + nsCOMPtr winSupports = + do_QueryInterface(nsDOMClassInfo::XPConnect()->GetNativeOfWrapper(cx, obj)); + if (!winSupports) + return true; + nsGlobalWindow *win = nsGlobalWindow::FromSupports(winSupports); + + // Find the child, if it exists. + nsCOMPtr child = win->GetChildWindow(id); + if (!child) + return true; + + // Wrap the child for JS. + jsval v; + nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), child, + /* aAllowWrapping = */ true, &v); + NS_ENSURE_SUCCESS(rv, false); + vp.set(v); + return true; +} + static nsHTMLDocument* GetDocument(JSObject *obj) { @@ -3674,6 +3701,27 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj, return JS_TRUE; } + nsGlobalWindow* win = static_cast(document->GetWindow()); + MOZ_ASSERT(win); + if (win->GetLength() > 0) { + nsCOMPtr child_win = win->GetChildWindow(id); + if (child_win) { + // We found a subframe of the right name, so define the property + // on the GSP. This property is a read-only accessor. Shadowing via + // |var foo| in global scope is still allowed, since |var| only looks + // up |own| properties. But unqualified shadowing will fail, per-spec. + if (!JS_DefinePropertyById(cx, obj, id, JS::UndefinedValue(), + ChildWindowGetter, JS_StrictPropertyStub, + JSPROP_SHARED | JSPROP_ENUMERATE)) + { + return false; + } + + objp.set(obj); + return true; + } + } + JSObject *proto; if (!::JS_GetPrototype(cx, obj, &proto)) { return JS_FALSE; @@ -5479,44 +5527,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, return NS_OK; } - // Hmm, we do an awful lot of QIs here; maybe we should add a - // method on an interface that would let us just call into the - // window code directly... - - if (!ObjectIsNativeWrapper(cx, obj) || - xpc::WrapperFactory::XrayWrapperNotShadowing(obj, id)) { - if (win->GetLength() > 0) { - nsCOMPtr child_win = win->GetChildWindow(id); - if (child_win) { - // We found a subframe of the right name, define the property - // on the wrapper so that ::NewResolve() doesn't get called - // again for this property name. - - JSObject *wrapperObj; - wrapper->GetJSObject(&wrapperObj); - - jsval v; - nsCOMPtr holder; - nsresult rv = WrapNative(cx, wrapperObj, child_win, - &NS_GET_IID(nsIDOMWindow), true, &v, - getter_AddRefs(holder)); - NS_ENSURE_SUCCESS(rv, rv); - - JSAutoRequest ar(cx); - - bool ok = JS_WrapValue(cx, &v) && - JS_DefinePropertyById(cx, obj, id, v, nullptr, nullptr, 0); - if (!ok) { - return NS_ERROR_FAILURE; - } - - *objp = obj; - - return NS_OK; - } - } - } - // Handle resolving if id refers to a name resolved by DOM worker code. JS::RootedObject tmp(cx, NULL); if (!ResolveWorkerClasses(cx, obj, id, flags, &tmp)) { From 730226df0649381d63a5dab0a87c53970680ba63 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 14 Mar 2013 22:38:27 -0700 Subject: [PATCH 031/202] Bug 850517 - Remove nsWindowSH::GetProperty. r=mrbkap It no longer does anything useful. --- dom/base/nsDOMClassInfo.cpp | 74 +------------------------------------ dom/base/nsDOMClassInfo.h | 2 - 2 files changed, 1 insertion(+), 75 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index c020b033ceb..ccd05b29b72 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -409,8 +409,7 @@ static const char kDOMStringBundleURL[] = // are defined in nsIDOMClassInfo.h. #define WINDOW_SCRIPTABLE_FLAGS \ - (nsIXPCScriptable::WANT_GETPROPERTY | \ - nsIXPCScriptable::WANT_PRECREATE | \ + (nsIXPCScriptable::WANT_PRECREATE | \ nsIXPCScriptable::WANT_FINALIZE | \ nsIXPCScriptable::WANT_ENUMERATE | \ nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \ @@ -3861,77 +3860,6 @@ nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JSObject *obj, return NS_OK; } -NS_IMETHODIMP -nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) -{ - DebugOnly win = nsGlobalWindow::FromWrapper(wrapper); - - JSAutoRequest ar(cx); - -#ifdef DEBUG_SH_FORWARDING - { - JSString *jsstr = ::JS_ValueToString(cx, id); - if (jsstr) { - nsDependentJSString str(jsstr); - - if (win->IsInnerWindow()) { -#ifdef DEBUG_PRINT_INNER - printf("Property '%s' get on inner window %p\n", - NS_ConvertUTF16toUTF8(str).get(), (void *)win); -#endif - } else { - printf("Property '%s' get on outer window %p\n", - NS_ConvertUTF16toUTF8(str).get(), (void *)win); - } - } - } -#endif - - // The order in which things are done in this method are a bit - // whacky, that's because this method is *extremely* performace - // critical. Don't touch this unless you know what you're doing. - - if (JSID_IS_STRING(id) && !JSVAL_IS_PRIMITIVE(*vp) && - ::JS_TypeOfValue(cx, *vp) != JSTYPE_FUNCTION) { - // A named property accessed which could have been resolved to a - // child frame in nsWindowSH::NewResolve() (*vp will tell us if - // that's the case). If *vp is a window object (i.e. a child - // frame), return without doing a security check. - // - // Calling GetWrappedNativeOfJSObject() is not all that cheap, so - // only do that if the JSClass name is one that is likely to be a - // window object. - - const char *name = JS_GetClass(JSVAL_TO_OBJECT(*vp))->name; - - // The list of Window class names here need to be kept in sync - // with the actual class names! The class name - // XPCCrossOriginWrapper needs to be handled here too as XOWs - // define child frame names with a XOW as the value, and thus - // we'll need to get through here with XOWs class name too. - if ((*name == 'W' && strcmp(name, "Window") == 0) || - (*name == 'C' && strcmp(name, "ChromeWindow") == 0) || - (*name == 'M' && strcmp(name, "ModalContentWindow") == 0) || - (*name == 'I' && - (strcmp(name, "InnerWindow") == 0 || - strcmp(name, "InnerChromeWindow") == 0 || - strcmp(name, "InnerModalContentWindow") == 0)) || - (*name == 'X' && strcmp(name, "XPCCrossOriginWrapper") == 0)) { - nsCOMPtr window = do_QueryWrapper(cx, JSVAL_TO_OBJECT(*vp)); - - if (window) { - // Yup, *vp is a window object, return early (*vp is already - // the window, so no need to wrap it again). - - return NS_SUCCESS_I_DID_SOMETHING; - } - } - } - - return NS_OK; -} - struct ResolveGlobalNameClosure { JSContext* cx; diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 555f5d9188d..e5c00b78af4 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -363,8 +363,6 @@ public: nsIXPCScriptable::WANT_POSTCREATE; } #endif - NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval); NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, bool *_retval); NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, From 2c89576a6bc4282a927430ecc718a3c390076eb7 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 14 Mar 2013 22:38:27 -0700 Subject: [PATCH 032/202] Bug 850517 - Propagate ifr.setAttribute('name', 'foo') to the docshell. r=bz This is correct per-spec. From HTML5's "The iframe element": "Whenever the name attribute is set, the nested browsing context's name must be changed to the new value. If the attribute is removed, the browsing context name must be set to the empty string." --- .../content/src/nsGenericHTMLFrameElement.cpp | 29 +++++++++++++++++++ .../content/src/nsGenericHTMLFrameElement.h | 4 ++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/content/html/content/src/nsGenericHTMLFrameElement.cpp b/content/html/content/src/nsGenericHTMLFrameElement.cpp index 214caaa802d..ae7ef112987 100644 --- a/content/html/content/src/nsGenericHTMLFrameElement.cpp +++ b/content/html/content/src/nsGenericHTMLFrameElement.cpp @@ -227,6 +227,35 @@ nsGenericHTMLFrameElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // Don't propagate error here. The attribute was successfully set, that's // what we should reflect. LoadSrc(); + } else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) { + // Propagate "name" to the docshell to make browsing context names live, + // per HTML5. + nsIDocShell *docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell() + : nullptr; + if (docShell) { + docShell->SetName(PromiseFlatString(aValue).get()); + } + } + + return NS_OK; +} + +nsresult +nsGenericHTMLFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) +{ + // Invoke on the superclass. + nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + + if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::name) { + // Propagate "name" to the docshell to make browsing context names live, + // per HTML5. + nsIDocShell *docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell() + : nullptr; + if (docShell) { + docShell->SetName(EmptyString().get()); + } } return NS_OK; diff --git a/content/html/content/src/nsGenericHTMLFrameElement.h b/content/html/content/src/nsGenericHTMLFrameElement.h index cef2f8254ee..2833e6dcc9a 100644 --- a/content/html/content/src/nsGenericHTMLFrameElement.h +++ b/content/html/content/src/nsGenericHTMLFrameElement.h @@ -53,7 +53,9 @@ public: } virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify); + bool aNotify) MOZ_OVERRIDE; + virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, + bool aNotify) MOZ_OVERRIDE; virtual void DestroyContent(); nsresult CopyInnerTo(mozilla::dom::Element* aDest); From 2eda513e4ce36194e40b43baa5a459c0b4fbeeba Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 14 Mar 2013 22:38:27 -0700 Subject: [PATCH 033/202] Bug 850517 - Tests. r=bz --- dom/tests/mochitest/bugs/Makefile.in | 1 + dom/tests/mochitest/bugs/test_bug850517.html | 47 ++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 dom/tests/mochitest/bugs/test_bug850517.html diff --git a/dom/tests/mochitest/bugs/Makefile.in b/dom/tests/mochitest/bugs/Makefile.in index 6533b96f24f..75d1cc25c88 100644 --- a/dom/tests/mochitest/bugs/Makefile.in +++ b/dom/tests/mochitest/bugs/Makefile.in @@ -140,6 +140,7 @@ MOCHITEST_FILES = \ test_protochains.html \ test_bug817476.html \ test_bug823173.html \ + test_bug850517.html \ $(NULL) ifneq (Linux,$(OS_ARCH)) diff --git a/dom/tests/mochitest/bugs/test_bug850517.html b/dom/tests/mochitest/bugs/test_bug850517.html new file mode 100644 index 00000000000..4ae076fd73a --- /dev/null +++ b/dom/tests/mochitest/bugs/test_bug850517.html @@ -0,0 +1,47 @@ + + + + + + Test for Bug 850517 + + + + + +Mozilla Bug 850517 +

+ + +
+
+ + From ee4f45a3aa6efa69bd98fb2194e8e20072792b55 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Fri, 15 Mar 2013 03:01:37 -0400 Subject: [PATCH 034/202] Bug 832155: Move SVGFEDistantLightElement to its own file r=Ms2ger --HG-- rename : content/svg/content/src/nsSVGFilters.cpp => content/svg/content/src/SVGFEDistantLightElement.cpp rename : content/svg/content/src/nsSVGFilters.cpp => content/svg/content/src/SVGFEDistantLightElement.h --- content/svg/content/src/Makefile.in | 2 + .../content/src/SVGFEDistantLightElement.cpp | 63 +++++++++++ .../content/src/SVGFEDistantLightElement.h | 47 ++++++++ content/svg/content/src/nsSVGFilters.cpp | 105 ------------------ 4 files changed, 112 insertions(+), 105 deletions(-) create mode 100644 content/svg/content/src/SVGFEDistantLightElement.cpp create mode 100644 content/svg/content/src/SVGFEDistantLightElement.h diff --git a/content/svg/content/src/Makefile.in b/content/svg/content/src/Makefile.in index 5b92b5c5387..be32fdff933 100644 --- a/content/svg/content/src/Makefile.in +++ b/content/svg/content/src/Makefile.in @@ -79,6 +79,7 @@ CPPSRCS = \ SVGElementFactory.cpp \ SVGEllipseElement.cpp \ SVGFEBlendElement.cpp \ + SVGFEDistantLightElement.cpp \ SVGFEFloodElement.cpp \ SVGFEImageElement.cpp \ SVGFEMergeElement.cpp \ @@ -177,6 +178,7 @@ EXPORTS_mozilla/dom = \ SVGDescElement.h \ SVGEllipseElement.h \ SVGFEBlendElement.h \ + SVGFEDistantLightElement.h \ SVGFEFloodElement.h \ SVGFEImageElement.h \ SVGFEMergeElement.h \ diff --git a/content/svg/content/src/SVGFEDistantLightElement.cpp b/content/svg/content/src/SVGFEDistantLightElement.cpp new file mode 100644 index 00000000000..9d66b48e1ea --- /dev/null +++ b/content/svg/content/src/SVGFEDistantLightElement.cpp @@ -0,0 +1,63 @@ +/* a*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGFEDistantLightElement.h" + +NS_IMPL_NS_NEW_SVG_ELEMENT(FEDistantLight) + +nsSVGElement::NumberInfo nsSVGFEDistantLightElement::sNumberInfo[2] = +{ + { &nsGkAtoms::azimuth, 0, false }, + { &nsGkAtoms::elevation, 0, false } +}; + +//---------------------------------------------------------------------- +// nsISupports methods + +NS_IMPL_ADDREF_INHERITED(nsSVGFEDistantLightElement,nsSVGFEDistantLightElementBase) +NS_IMPL_RELEASE_INHERITED(nsSVGFEDistantLightElement,nsSVGFEDistantLightElementBase) + +DOMCI_NODE_DATA(SVGFEDistantLightElement, nsSVGFEDistantLightElement) + +NS_INTERFACE_TABLE_HEAD(nsSVGFEDistantLightElement) + NS_NODE_INTERFACE_TABLE4(nsSVGFEDistantLightElement, nsIDOMNode, + nsIDOMElement, nsIDOMSVGElement, + nsIDOMSVGFEDistantLightElement) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEDistantLightElement) +NS_INTERFACE_MAP_END_INHERITING(nsSVGFEDistantLightElementBase) + +//---------------------------------------------------------------------- +// nsIDOMNode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEDistantLightElement) + +//---------------------------------------------------------------------- +// nsFEUnstyledElement methods + +bool +nsSVGFEDistantLightElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const +{ + return aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::azimuth || + aAttribute == nsGkAtoms::elevation); +} + +//---------------------------------------------------------------------- +// nsIDOMSVGFEDistantLightElement methods + +NS_IMETHODIMP +nsSVGFEDistantLightElement::GetAzimuth(nsIDOMSVGAnimatedNumber **aAzimuth) +{ + return mNumberAttributes[AZIMUTH].ToDOMAnimatedNumber(aAzimuth, + this); +} + +NS_IMETHODIMP +nsSVGFEDistantLightElement::GetElevation(nsIDOMSVGAnimatedNumber **aElevation) +{ + return mNumberAttributes[ELEVATION].ToDOMAnimatedNumber(aElevation, + this); +} diff --git a/content/svg/content/src/SVGFEDistantLightElement.h b/content/svg/content/src/SVGFEDistantLightElement.h new file mode 100644 index 00000000000..c480626c0ea --- /dev/null +++ b/content/svg/content/src/SVGFEDistantLightElement.h @@ -0,0 +1,47 @@ +/* a*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 mozilla_dom_SVGFEDistantLightElement_h +#define mozilla_dom_SVGFEDistantLightElement_h + +#include "nsSVGFilters.h" + +typedef SVGFEUnstyledElement nsSVGFEDistantLightElementBase; + +class nsSVGFEDistantLightElement : public nsSVGFEDistantLightElementBase, + public nsIDOMSVGFEDistantLightElement +{ + friend nsresult NS_NewSVGFEDistantLightElement(nsIContent **aResult, + already_AddRefed aNodeInfo); +protected: + nsSVGFEDistantLightElement(already_AddRefed aNodeInfo) + : nsSVGFEDistantLightElementBase(aNodeInfo) {} + +public: + // interfaces: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIDOMSVGFEDISTANTLIGHTELEMENT + + NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEDistantLightElementBase::) + NS_FORWARD_NSIDOMNODE_TO_NSINODE + NS_FORWARD_NSIDOMELEMENT_TO_GENERIC + + virtual bool AttributeAffectsRendering( + int32_t aNameSpaceID, nsIAtom* aAttribute) const; + + virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; + + virtual nsXPCClassInfo* GetClassInfo(); + + virtual nsIDOMNode* AsDOMNode() { return this; } +protected: + virtual NumberAttributesInfo GetNumberInfo(); + + enum { AZIMUTH, ELEVATION }; + nsSVGNumber2 mNumberAttributes[2]; + static NumberInfo sNumberInfo[2]; +}; + +#endif // mozilla_dom_SVGFEDistantLightElement_h diff --git a/content/svg/content/src/nsSVGFilters.cpp b/content/svg/content/src/nsSVGFilters.cpp index 736db98aa01..61a730db5d2 100644 --- a/content/svg/content/src/nsSVGFilters.cpp +++ b/content/svg/content/src/nsSVGFilters.cpp @@ -3565,111 +3565,6 @@ nsSVGFEConvolveMatrixElement::GetNumberListInfo() ArrayLength(sNumberListInfo)); } -//---------------------DistantLight------------------------ - -typedef SVGFEUnstyledElement nsSVGFEDistantLightElementBase; - -class nsSVGFEDistantLightElement : public nsSVGFEDistantLightElementBase, - public nsIDOMSVGFEDistantLightElement -{ - friend nsresult NS_NewSVGFEDistantLightElement(nsIContent **aResult, - already_AddRefed aNodeInfo); -protected: - nsSVGFEDistantLightElement(already_AddRefed aNodeInfo) - : nsSVGFEDistantLightElementBase(aNodeInfo) {} - -public: - // interfaces: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIDOMSVGFEDISTANTLIGHTELEMENT - - NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEDistantLightElementBase::) - NS_FORWARD_NSIDOMNODE_TO_NSINODE - NS_FORWARD_NSIDOMELEMENT_TO_GENERIC - - virtual bool AttributeAffectsRendering( - int32_t aNameSpaceID, nsIAtom* aAttribute) const; - - virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; - - virtual nsXPCClassInfo* GetClassInfo(); - - virtual nsIDOMNode* AsDOMNode() { return this; } -protected: - virtual NumberAttributesInfo GetNumberInfo(); - - enum { AZIMUTH, ELEVATION }; - nsSVGNumber2 mNumberAttributes[2]; - static NumberInfo sNumberInfo[2]; -}; - -NS_IMPL_NS_NEW_SVG_ELEMENT(FEDistantLight) - -nsSVGElement::NumberInfo nsSVGFEDistantLightElement::sNumberInfo[2] = -{ - { &nsGkAtoms::azimuth, 0, false }, - { &nsGkAtoms::elevation, 0, false } -}; - -//---------------------------------------------------------------------- -// nsISupports methods - -NS_IMPL_ADDREF_INHERITED(nsSVGFEDistantLightElement,nsSVGFEDistantLightElementBase) -NS_IMPL_RELEASE_INHERITED(nsSVGFEDistantLightElement,nsSVGFEDistantLightElementBase) - -DOMCI_NODE_DATA(SVGFEDistantLightElement, nsSVGFEDistantLightElement) - -NS_INTERFACE_TABLE_HEAD(nsSVGFEDistantLightElement) - NS_NODE_INTERFACE_TABLE4(nsSVGFEDistantLightElement, nsIDOMNode, - nsIDOMElement, nsIDOMSVGElement, - nsIDOMSVGFEDistantLightElement) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEDistantLightElement) -NS_INTERFACE_MAP_END_INHERITING(nsSVGFEDistantLightElementBase) - -//---------------------------------------------------------------------- -// nsIDOMNode methods - -NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEDistantLightElement) - -//---------------------------------------------------------------------- -// nsFEUnstyledElement methods - -bool -nsSVGFEDistantLightElement::AttributeAffectsRendering(int32_t aNameSpaceID, - nsIAtom* aAttribute) const -{ - return aNameSpaceID == kNameSpaceID_None && - (aAttribute == nsGkAtoms::azimuth || - aAttribute == nsGkAtoms::elevation); -} - -//---------------------------------------------------------------------- -// nsIDOMSVGFEDistantLightElement methods - -NS_IMETHODIMP -nsSVGFEDistantLightElement::GetAzimuth(nsIDOMSVGAnimatedNumber **aAzimuth) -{ - return mNumberAttributes[AZIMUTH].ToDOMAnimatedNumber(aAzimuth, - this); -} - -NS_IMETHODIMP -nsSVGFEDistantLightElement::GetElevation(nsIDOMSVGAnimatedNumber **aElevation) -{ - return mNumberAttributes[ELEVATION].ToDOMAnimatedNumber(aElevation, - this); -} - -//---------------------------------------------------------------------- -// nsSVGElement methods - -nsSVGElement::NumberAttributesInfo -nsSVGFEDistantLightElement::GetNumberInfo() -{ - return NumberAttributesInfo(mNumberAttributes, sNumberInfo, - ArrayLength(sNumberInfo)); -} - //---------------------SpotLight------------------------ typedef SVGFEUnstyledElement nsSVGFESpotLightElementBase; From 91d2d6293e4a16f4ce0cea4484a0e996a533fe36 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Fri, 15 Mar 2013 03:01:38 -0400 Subject: [PATCH 035/202] Bug 832155: Convert SVGFEDistantLightElement to WebIDL r=Ms2ger --- .../content/src/SVGFEDistantLightElement.cpp | 66 +++++++++++-------- .../content/src/SVGFEDistantLightElement.h | 38 +++++++---- dom/base/nsDOMClassInfo.cpp | 7 -- dom/base/nsDOMClassInfoClasses.h | 1 - dom/interfaces/svg/nsIDOMSVGFilters.idl | 6 -- dom/webidl/SVGFEDistantLightElement.webidl | 18 +++++ dom/webidl/WebIDL.mk | 1 + 7 files changed, 86 insertions(+), 51 deletions(-) create mode 100644 dom/webidl/SVGFEDistantLightElement.webidl diff --git a/content/svg/content/src/SVGFEDistantLightElement.cpp b/content/svg/content/src/SVGFEDistantLightElement.cpp index 9d66b48e1ea..e610f3ce7b0 100644 --- a/content/svg/content/src/SVGFEDistantLightElement.cpp +++ b/content/svg/content/src/SVGFEDistantLightElement.cpp @@ -4,10 +4,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/SVGFEDistantLightElement.h" +#include "mozilla/dom/SVGFEDistantLightElementBinding.h" -NS_IMPL_NS_NEW_SVG_ELEMENT(FEDistantLight) +NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEDistantLight) -nsSVGElement::NumberInfo nsSVGFEDistantLightElement::sNumberInfo[2] = +namespace mozilla { +namespace dom { + +JSObject* +SVGFEDistantLightElement::WrapNode(JSContext* aCx, JSObject* aScope) +{ + return SVGFEDistantLightElementBinding::Wrap(aCx, aScope, this); +} + +nsSVGElement::NumberInfo SVGFEDistantLightElement::sNumberInfo[2] = { { &nsGkAtoms::azimuth, 0, false }, { &nsGkAtoms::elevation, 0, false } @@ -16,48 +26,52 @@ nsSVGElement::NumberInfo nsSVGFEDistantLightElement::sNumberInfo[2] = //---------------------------------------------------------------------- // nsISupports methods -NS_IMPL_ADDREF_INHERITED(nsSVGFEDistantLightElement,nsSVGFEDistantLightElementBase) -NS_IMPL_RELEASE_INHERITED(nsSVGFEDistantLightElement,nsSVGFEDistantLightElementBase) +NS_IMPL_ADDREF_INHERITED(SVGFEDistantLightElement,SVGFEDistantLightElementBase) +NS_IMPL_RELEASE_INHERITED(SVGFEDistantLightElement,SVGFEDistantLightElementBase) -DOMCI_NODE_DATA(SVGFEDistantLightElement, nsSVGFEDistantLightElement) - -NS_INTERFACE_TABLE_HEAD(nsSVGFEDistantLightElement) - NS_NODE_INTERFACE_TABLE4(nsSVGFEDistantLightElement, nsIDOMNode, - nsIDOMElement, nsIDOMSVGElement, - nsIDOMSVGFEDistantLightElement) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEDistantLightElement) -NS_INTERFACE_MAP_END_INHERITING(nsSVGFEDistantLightElementBase) +NS_INTERFACE_TABLE_HEAD(SVGFEDistantLightElement) + NS_NODE_INTERFACE_TABLE3(SVGFEDistantLightElement, nsIDOMNode, + nsIDOMElement, nsIDOMSVGElement) +NS_INTERFACE_MAP_END_INHERITING(SVGFEDistantLightElementBase) //---------------------------------------------------------------------- // nsIDOMNode methods -NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEDistantLightElement) +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEDistantLightElement) //---------------------------------------------------------------------- // nsFEUnstyledElement methods bool -nsSVGFEDistantLightElement::AttributeAffectsRendering(int32_t aNameSpaceID, - nsIAtom* aAttribute) const +SVGFEDistantLightElement::AttributeAffectsRendering(int32_t aNameSpaceID, + nsIAtom* aAttribute) const { return aNameSpaceID == kNameSpaceID_None && (aAttribute == nsGkAtoms::azimuth || aAttribute == nsGkAtoms::elevation); } +already_AddRefed +SVGFEDistantLightElement::Azimuth() +{ + return mNumberAttributes[AZIMUTH].ToDOMAnimatedNumber(this); +} + +already_AddRefed +SVGFEDistantLightElement::Elevation() +{ + return mNumberAttributes[ELEVATION].ToDOMAnimatedNumber(this); +} + //---------------------------------------------------------------------- -// nsIDOMSVGFEDistantLightElement methods +// nsSVGElement methods -NS_IMETHODIMP -nsSVGFEDistantLightElement::GetAzimuth(nsIDOMSVGAnimatedNumber **aAzimuth) +nsSVGElement::NumberAttributesInfo +SVGFEDistantLightElement::GetNumberInfo() { - return mNumberAttributes[AZIMUTH].ToDOMAnimatedNumber(aAzimuth, - this); + return NumberAttributesInfo(mNumberAttributes, sNumberInfo, + ArrayLength(sNumberInfo)); } -NS_IMETHODIMP -nsSVGFEDistantLightElement::GetElevation(nsIDOMSVGAnimatedNumber **aElevation) -{ - return mNumberAttributes[ELEVATION].ToDOMAnimatedNumber(aElevation, - this); -} +} // namespace dom +} // namespace mozilla diff --git a/content/svg/content/src/SVGFEDistantLightElement.h b/content/svg/content/src/SVGFEDistantLightElement.h index c480626c0ea..675c9845456 100644 --- a/content/svg/content/src/SVGFEDistantLightElement.h +++ b/content/svg/content/src/SVGFEDistantLightElement.h @@ -7,24 +7,34 @@ #define mozilla_dom_SVGFEDistantLightElement_h #include "nsSVGFilters.h" +#include "nsSVGNumber2.h" -typedef SVGFEUnstyledElement nsSVGFEDistantLightElementBase; +typedef SVGFEUnstyledElement SVGFEDistantLightElementBase; -class nsSVGFEDistantLightElement : public nsSVGFEDistantLightElementBase, - public nsIDOMSVGFEDistantLightElement +nsresult NS_NewSVGFEDistantLightElement(nsIContent **aResult, + already_AddRefed aNodeInfo); + +namespace mozilla { +namespace dom { + +class SVGFEDistantLightElement : public SVGFEDistantLightElementBase, + public nsIDOMSVGElement { - friend nsresult NS_NewSVGFEDistantLightElement(nsIContent **aResult, - already_AddRefed aNodeInfo); + friend nsresult (::NS_NewSVGFEDistantLightElement(nsIContent **aResult, + already_AddRefed aNodeInfo)); protected: - nsSVGFEDistantLightElement(already_AddRefed aNodeInfo) - : nsSVGFEDistantLightElementBase(aNodeInfo) {} + SVGFEDistantLightElement(already_AddRefed aNodeInfo) + : SVGFEDistantLightElementBase(aNodeInfo) + { + SetIsDOMBinding(); + } + virtual JSObject* WrapNode(JSContext* aCx, JSObject* aScope) MOZ_OVERRIDE; public: // interfaces: NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIDOMSVGFEDISTANTLIGHTELEMENT - NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEDistantLightElementBase::) + NS_FORWARD_NSIDOMSVGELEMENT(SVGFEDistantLightElementBase::) NS_FORWARD_NSIDOMNODE_TO_NSINODE NS_FORWARD_NSIDOMELEMENT_TO_GENERIC @@ -33,9 +43,12 @@ public: virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; - virtual nsXPCClassInfo* GetClassInfo(); - virtual nsIDOMNode* AsDOMNode() { return this; } + + // WebIDL + already_AddRefed Azimuth(); + already_AddRefed Elevation(); + protected: virtual NumberAttributesInfo GetNumberInfo(); @@ -44,4 +57,7 @@ protected: static NumberInfo sNumberInfo[2]; }; +} // namespace dom +} // namespace mozilla + #endif // mozilla_dom_SVGFEDistantLightElement_h diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index ccd05b29b72..1340b1a3f1e 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -828,8 +828,6 @@ static nsDOMClassInfoData sClassInfoData[] = { ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGFEDisplacementMapElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(SVGFEDistantLightElement, nsElementSH, - ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGFEGaussianBlurElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGFEMorphologyElement, nsElementSH, @@ -2296,11 +2294,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(SVGFEDistantLightElement, nsIDOMSVGFEDistantLightElement) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGFEDistantLightElement) - DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES - DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(SVGFEGaussianBlurElement, nsIDOMSVGFEGaussianBlurElement) DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGFEGaussianBlurElement) DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGFilterPrimitiveStandardAttributes) diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index ef3e4beea42..7dc3ba3ce27 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -128,7 +128,6 @@ DOMCI_CLASS(SVGFECompositeElement) DOMCI_CLASS(SVGFEConvolveMatrixElement) DOMCI_CLASS(SVGFEDiffuseLightingElement) DOMCI_CLASS(SVGFEDisplacementMapElement) -DOMCI_CLASS(SVGFEDistantLightElement) DOMCI_CLASS(SVGFEGaussianBlurElement) DOMCI_CLASS(SVGFEMorphologyElement) DOMCI_CLASS(SVGFEOffsetElement) diff --git a/dom/interfaces/svg/nsIDOMSVGFilters.idl b/dom/interfaces/svg/nsIDOMSVGFilters.idl index f1c03e300ec..5ac03d86702 100644 --- a/dom/interfaces/svg/nsIDOMSVGFilters.idl +++ b/dom/interfaces/svg/nsIDOMSVGFilters.idl @@ -206,12 +206,6 @@ interface nsIDOMSVGFESpecularLightingElement : nsIDOMSVGFilterPrimitiveStandardA readonly attribute nsIDOMSVGAnimatedNumber kernelUnitLengthY; }; -[scriptable, uuid(3265edba-d5b0-4c24-ad89-d6b6c85655bf)] -interface nsIDOMSVGFEDistantLightElement : nsIDOMSVGElement { - readonly attribute nsIDOMSVGAnimatedNumber azimuth; - readonly attribute nsIDOMSVGAnimatedNumber elevation; -}; - [scriptable, uuid(e9bd8308-dc52-438b-a315-a0d545dfeda3)] interface nsIDOMSVGFESpotLightElement : nsIDOMSVGElement { readonly attribute nsIDOMSVGAnimatedNumber x; diff --git a/dom/webidl/SVGFEDistantLightElement.webidl b/dom/webidl/SVGFEDistantLightElement.webidl new file mode 100644 index 00000000000..9a1f881d626 --- /dev/null +++ b/dom/webidl/SVGFEDistantLightElement.webidl @@ -0,0 +1,18 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * http://www.w3.org/TR/SVG2/ + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +interface SVGAnimatedNumber; + +interface SVGFEDistantLightElement : SVGElement { + readonly attribute SVGAnimatedNumber azimuth; + readonly attribute SVGAnimatedNumber elevation; +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 75e3e0e07d0..6b2b69b5f95 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -168,6 +168,7 @@ webidl_files = \ SVGFilterElement.webidl \ SVGFilterPrimitiveStandardAttributes.webidl \ SVGFEBlendElement.webidl \ + SVGFEDistantLightElement.webidl \ SVGFEFloodElement.webidl \ SVGFEFuncAElement.webidl \ SVGFEFuncBElement.webidl \ From feb121ed5750ac410f3653a022697a95d56e9b6d Mon Sep 17 00:00:00 2001 From: Matthew Noorenberghe Date: Thu, 14 Mar 2013 23:59:06 -0700 Subject: [PATCH 036/202] Bug 851040 - Try to fix intermittent browser/components/search/test/browser_contextmenu.js. a=test-only --HG-- extra : rebase_source : b147ecee975683729e21d1fdc231b33e026865c8 --- .../search/test/browser_contextmenu.js | 17 ++++++++++------- browser/components/search/test/head.js | 8 +++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/browser/components/search/test/browser_contextmenu.js b/browser/components/search/test/browser_contextmenu.js index eacfb0c9c10..05635981880 100644 --- a/browser/components/search/test/browser_contextmenu.js +++ b/browser/components/search/test/browser_contextmenu.js @@ -41,8 +41,7 @@ function test() { ok(contextMenu, "Got context menu XUL"); doOnloadOnce(testContextMenu); - var tab = gBrowser.addTab("data:text/plain;charset=utf8,test%20search"); - gBrowser.selectedTab = tab; + gBrowser.selectedTab = gBrowser.addTab("data:text/plain;charset=utf8,test%20search"); } function testContextMenu() { @@ -84,11 +83,15 @@ function test() { } }; - // add a listener to know when the selection takes effect - content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate). - addSelectionListener(selectionListener); - // select the text on the page - goDoCommand('cmd_selectAll'); + // Delay the select all to avoid intermittent selection failures. + setTimeout(function delaySelectAll() { + info("delaySelectAll: " + content.window.location.toString()); + // add a listener to know when the selection takes effect + content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate). + addSelectionListener(selectionListener); + // select the text on the page + goDoCommand('cmd_selectAll'); + }, 500); } function finalize() { diff --git a/browser/components/search/test/head.js b/browser/components/search/test/head.js index 471d23a3f30..f9db478794c 100644 --- a/browser/components/search/test/head.js +++ b/browser/components/search/test/head.js @@ -41,11 +41,13 @@ function doOnloadOnce(aCallback) { function doOnloadOnceListener(aEvent) { info("doOnloadOnce: " + aEvent.originalTarget.location); removeDoOnloadOnceListener(); - aCallback(aEvent); + SimpleTest.executeSoon(function doOnloadOnceCallback() { + aCallback(aEvent); + }); } function removeDoOnloadOnceListener() { - gBrowser.removeEventListener("DOMContentLoaded", doOnloadOnceListener); + gBrowser.removeEventListener("load", doOnloadOnceListener, true); } - gBrowser.addEventListener("DOMContentLoaded", doOnloadOnceListener); + gBrowser.addEventListener("load", doOnloadOnceListener, true); registerCleanupFunction(removeDoOnloadOnceListener); } From dd9199bb561facb6fb42429b4e5bebaf91b0c818 Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:21:47 +0800 Subject: [PATCH 037/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 1, nsIDOMMobileMessageManager). r=vicamo sr=sicking a=leo+ --HG-- rename : dom/mobilemessage/interfaces/nsIDOMSmsManager.idl => dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl rename : dom/mobilemessage/interfaces/nsIDOMNavigatorSms.idl => dom/mobilemessage/interfaces/nsIDOMNavigatorMobileMessage.idl rename : dom/mobilemessage/src/SmsManager.cpp => dom/mobilemessage/src/MobileMessageManager.cpp rename : dom/mobilemessage/src/SmsManager.h => dom/mobilemessage/src/MobileMessageManager.h --- dom/base/Navigator.cpp | 42 ++ dom/base/Navigator.h | 5 + dom/base/nsDOMClassInfo.cpp | 9 + dom/base/nsDOMClassInfoClasses.h | 1 + dom/mobilemessage/interfaces/moz.build | 2 + .../interfaces/nsIDOMMobileMessageManager.idl | 41 ++ .../nsIDOMNavigatorMobileMessage.idl | 13 + dom/mobilemessage/src/Makefile.in | 2 + .../src/MobileMessageManager.cpp | 367 ++++++++++++++++++ dom/mobilemessage/src/MobileMessageManager.h | 51 +++ dom/mobilemessage/src/SmsRequest.cpp | 18 + dom/mobilemessage/src/SmsRequest.h | 3 + .../mochitest/general/test_interfaces.html | 2 + 13 files changed, 556 insertions(+) create mode 100644 dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl create mode 100644 dom/mobilemessage/interfaces/nsIDOMNavigatorMobileMessage.idl create mode 100644 dom/mobilemessage/src/MobileMessageManager.cpp create mode 100644 dom/mobilemessage/src/MobileMessageManager.h diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index b547a8de3cf..e3d3f63c4af 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -30,6 +30,7 @@ #include "nsIDOMWakeLock.h" #include "nsIPowerManagerService.h" #include "mozilla/dom/SmsManager.h" +#include "mozilla/dom/MobileMessageManager.h" #include "nsISmsService.h" #include "mozilla/Hal.h" #include "nsIWebNavigation.h" @@ -123,6 +124,7 @@ NS_INTERFACE_MAP_BEGIN(Navigator) NS_INTERFACE_MAP_ENTRY(nsINavigatorBattery) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDesktopNotification) NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorSms) + NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorMobileMessage) NS_INTERFACE_MAP_ENTRY(nsIObserver) #ifdef MOZ_MEDIA_NAVIGATOR NS_INTERFACE_MAP_ENTRY(nsINavigatorUserMedia) @@ -196,6 +198,11 @@ Navigator::Invalidate() mSmsManager = nullptr; } + if (mMobileMessageManager) { + mMobileMessageManager->Shutdown(); + mMobileMessageManager = nullptr; + } + #ifdef MOZ_B2G_RIL if (mTelephony) { mTelephony = nullptr; @@ -1196,6 +1203,41 @@ Navigator::GetMozSms(nsIDOMMozSmsManager** aSmsManager) return NS_OK; } +//***************************************************************************** +// Navigator::nsIDOMNavigatorMobileMessage +//***************************************************************************** + +NS_IMETHODIMP +Navigator::GetMozMobileMessage(nsIDOMMozMobileMessageManager** aMobileMessageManager) +{ + *aMobileMessageManager = nullptr; + +#ifndef MOZ_WEBSMS_BACKEND + return NS_OK; +#endif + + // First of all, the general pref has to be turned on. + bool enabled = false; + Preferences::GetBool("dom.sms.enabled", &enabled); + NS_ENSURE_TRUE(enabled, NS_OK); + + if (!mMobileMessageManager) { + nsCOMPtr window = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(window && window->GetDocShell(), NS_OK); + + if (!CheckPermission("sms")) { + return NS_OK; + } + + mMobileMessageManager = new MobileMessageManager(); + mMobileMessageManager->Init(window); + } + + NS_ADDREF(*aMobileMessageManager = mMobileMessageManager); + + return NS_OK; +} + #ifdef MOZ_B2G_RIL //***************************************************************************** diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 582fb5effab..54500f36a43 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -14,6 +14,7 @@ #include "nsIDOMClientInformation.h" #include "nsINavigatorBattery.h" #include "nsIDOMNavigatorSms.h" +#include "nsIDOMNavigatorMobileMessage.h" #include "nsIDOMNavigatorNetwork.h" #include "nsIObserver.h" #ifdef MOZ_AUDIO_CHANNEL_MANAGER @@ -66,6 +67,7 @@ class BatteryManager; } // namespace battery class SmsManager; +class MobileMessageManager; namespace network { class Connection; @@ -95,6 +97,7 @@ class Navigator : public nsIDOMNavigator , public nsIDOMNavigatorDesktopNotification , public nsINavigatorBattery , public nsIDOMMozNavigatorSms + , public nsIDOMMozNavigatorMobileMessage , public nsIObserver #ifdef MOZ_MEDIA_NAVIGATOR , public nsINavigatorUserMedia @@ -133,6 +136,7 @@ public: NS_DECL_NSIDOMNAVIGATORDESKTOPNOTIFICATION NS_DECL_NSINAVIGATORBATTERY NS_DECL_NSIDOMMOZNAVIGATORSMS + NS_DECL_NSIDOMMOZNAVIGATORMOBILEMESSAGE NS_DECL_NSIOBSERVER #ifdef MOZ_MEDIA_NAVIGATOR NS_DECL_NSINAVIGATORUSERMEDIA @@ -196,6 +200,7 @@ private: nsRefPtr mBatteryManager; nsRefPtr mPowerManager; nsRefPtr mSmsManager; + nsRefPtr mMobileMessageManager; #ifdef MOZ_B2G_RIL nsCOMPtr mTelephony; nsCOMPtr mVoicemail; diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 1340b1a3f1e..022e89af2bc 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -336,6 +336,7 @@ using mozilla::dom::workers::ResolveWorkerClasses; #include "nsIDOMPowerManager.h" #include "nsIDOMWakeLock.h" #include "nsIDOMSmsManager.h" +#include "nsIDOMMobileMessageManager.h" #include "nsIDOMMozSmsMessage.h" #include "nsIDOMSmsRequest.h" #include "nsIDOMSmsFilter.h" @@ -966,6 +967,9 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(MozSmsManager, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(MozMobileMessageManager, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(MozSmsMessage, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -1859,6 +1863,7 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsINavigatorBattery, battery::BatteryManager::HasSupport()) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozNavigatorSms) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozNavigatorMobileMessage) #ifdef MOZ_MEDIA_NAVIGATOR DOM_CLASSINFO_MAP_ENTRY(nsINavigatorUserMedia) DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorUserMedia) @@ -2519,6 +2524,10 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsManager) DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(MozMobileMessageManager, nsIDOMMozMobileMessageManager) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozMobileMessageManager) + DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(MozSmsMessage, nsIDOMMozSmsMessage) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsMessage) DOM_CLASSINFO_MAP_END diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index 7dc3ba3ce27..d0b122e49c7 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -205,6 +205,7 @@ DOMCI_CLASS(MozPowerManager) DOMCI_CLASS(MozWakeLock) DOMCI_CLASS(MozSmsManager) +DOMCI_CLASS(MozMobileMessageManager) DOMCI_CLASS(MozSmsMessage) DOMCI_CLASS(MozSmsRequest) DOMCI_CLASS(MozSmsFilter) diff --git a/dom/mobilemessage/interfaces/moz.build b/dom/mobilemessage/interfaces/moz.build index f97c134b1aa..4bab624506c 100644 --- a/dom/mobilemessage/interfaces/moz.build +++ b/dom/mobilemessage/interfaces/moz.build @@ -4,8 +4,10 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. XPIDL_SOURCES += [ + 'nsIDOMMobileMessageManager.idl', 'nsIDOMMozSmsEvent.idl', 'nsIDOMMozSmsMessage.idl', + 'nsIDOMNavigatorMobileMessage.idl', 'nsIDOMNavigatorSms.idl', 'nsIDOMSmsCursor.idl', 'nsIDOMSmsFilter.idl', diff --git a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl new file mode 100644 index 00000000000..1cf2be8f7f1 --- /dev/null +++ b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIDOMEventTarget.idl" + +interface nsIDOMEventListener; +interface nsIDOMMozSmsRequest; +interface nsIDOMMozSmsFilter; +interface nsIDOMMozSmsSegmentInfo; + +[scriptable, builtinclass, uuid(228508d0-7fe4-11e2-a028-83810f98f20b)] +interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget +{ + nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text); + + // The first parameter can be either a DOMString (only one number) or an array + // of DOMStrings. + // The method returns a SmsRequest object if one number has been passed. + // An array of SmsRequest objects otherwise. + jsval send(in jsval number, in DOMString message); + + [binaryname(GetMessageMoz)] + nsIDOMMozSmsRequest getMessage(in long id); + + // The parameter can be either a message id or a SmsMessage. + nsIDOMMozSmsRequest delete(in jsval param); + + nsIDOMMozSmsRequest getMessages(in nsIDOMMozSmsFilter filter, in boolean reverse); + + nsIDOMMozSmsRequest markMessageRead(in long id, in boolean aValue); + + nsIDOMMozSmsRequest getThreadList(); + + [implicit_jscontext] attribute jsval onreceived; + [implicit_jscontext] attribute jsval onsending; + [implicit_jscontext] attribute jsval onsent; + [implicit_jscontext] attribute jsval onfailed; + [implicit_jscontext] attribute jsval ondeliverysuccess; + [implicit_jscontext] attribute jsval ondeliveryerror; +}; diff --git a/dom/mobilemessage/interfaces/nsIDOMNavigatorMobileMessage.idl b/dom/mobilemessage/interfaces/nsIDOMNavigatorMobileMessage.idl new file mode 100644 index 00000000000..47b2db74117 --- /dev/null +++ b/dom/mobilemessage/interfaces/nsIDOMNavigatorMobileMessage.idl @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIDOMMozMobileMessageManager; + +[scriptable, uuid(baefbb1a-7fe4-11e2-abff-cf1a90f9139f)] +interface nsIDOMMozNavigatorMobileMessage : nsISupports +{ + readonly attribute nsIDOMMozMobileMessageManager mozMobileMessage; +}; diff --git a/dom/mobilemessage/src/Makefile.in b/dom/mobilemessage/src/Makefile.in index cc1f06deb29..9c3c554c35f 100644 --- a/dom/mobilemessage/src/Makefile.in +++ b/dom/mobilemessage/src/Makefile.in @@ -36,6 +36,7 @@ EXPORTS_NAMESPACES = \ EXPORTS_mozilla/dom = \ SmsManager.h \ + MobileMessageManager.h \ SmsMessage.h \ SmsRequest.h \ SmsSegmentInfo.h \ @@ -52,6 +53,7 @@ EXPORTS_mozilla/dom/mobilemessage = \ CPPSRCS = \ SmsManager.cpp \ + MobileMessageManager.cpp \ SmsService.cpp \ SmsIPCService.cpp \ SmsServicesFactory.cpp \ diff --git a/dom/mobilemessage/src/MobileMessageManager.cpp b/dom/mobilemessage/src/MobileMessageManager.cpp new file mode 100644 index 00000000000..ebb21c410f9 --- /dev/null +++ b/dom/mobilemessage/src/MobileMessageManager.cpp @@ -0,0 +1,367 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SmsFilter.h" +#include "MobileMessageManager.h" +#include "nsIDOMClassInfo.h" +#include "nsISmsService.h" +#include "nsIObserverService.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "Constants.h" +#include "nsIDOMMozSmsEvent.h" +#include "nsIDOMMozSmsMessage.h" +#include "SmsRequest.h" +#include "nsJSUtils.h" +#include "nsContentUtils.h" +#include "nsIMobileMessageDatabaseService.h" +#include "nsIXPConnect.h" +#include "nsIPermissionManager.h" +#include "GeneratedEvents.h" + +#define RECEIVED_EVENT_NAME NS_LITERAL_STRING("received") +#define SENDING_EVENT_NAME NS_LITERAL_STRING("sending") +#define SENT_EVENT_NAME NS_LITERAL_STRING("sent") +#define FAILED_EVENT_NAME NS_LITERAL_STRING("failed") +#define DELIVERY_SUCCESS_EVENT_NAME NS_LITERAL_STRING("deliverysuccess") +#define DELIVERY_ERROR_EVENT_NAME NS_LITERAL_STRING("deliveryerror") + +using namespace mozilla::dom::mobilemessage; + +DOMCI_DATA(MozMobileMessageManager, mozilla::dom::MobileMessageManager) + +namespace mozilla { +namespace dom { + +NS_INTERFACE_MAP_BEGIN(MobileMessageManager) + NS_INTERFACE_MAP_ENTRY(nsIDOMMozMobileMessageManager) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozMobileMessageManager) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(MobileMessageManager, nsDOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(MobileMessageManager, nsDOMEventTargetHelper) + +NS_IMPL_EVENT_HANDLER(MobileMessageManager, received) +NS_IMPL_EVENT_HANDLER(MobileMessageManager, sending) +NS_IMPL_EVENT_HANDLER(MobileMessageManager, sent) +NS_IMPL_EVENT_HANDLER(MobileMessageManager, failed) +NS_IMPL_EVENT_HANDLER(MobileMessageManager, deliverysuccess) +NS_IMPL_EVENT_HANDLER(MobileMessageManager, deliveryerror) + +void +MobileMessageManager::Init(nsPIDOMWindow *aWindow) +{ + BindToOwner(aWindow); + + nsCOMPtr obs = services::GetObserverService(); + // GetObserverService() can return null is some situations like shutdown. + if (!obs) { + return; + } + + obs->AddObserver(this, kSmsReceivedObserverTopic, false); + obs->AddObserver(this, kSmsSendingObserverTopic, false); + obs->AddObserver(this, kSmsSentObserverTopic, false); + obs->AddObserver(this, kSmsFailedObserverTopic, false); + obs->AddObserver(this, kSmsDeliverySuccessObserverTopic, false); + obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false); +} + +void +MobileMessageManager::Shutdown() +{ + nsCOMPtr obs = services::GetObserverService(); + // GetObserverService() can return null is some situations like shutdown. + if (!obs) { + return; + } + + obs->RemoveObserver(this, kSmsReceivedObserverTopic); + obs->RemoveObserver(this, kSmsSendingObserverTopic); + obs->RemoveObserver(this, kSmsSentObserverTopic); + obs->RemoveObserver(this, kSmsFailedObserverTopic); + obs->RemoveObserver(this, kSmsDeliverySuccessObserverTopic); + obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic); +} + +NS_IMETHODIMP +MobileMessageManager::GetSegmentInfoForText(const nsAString& aText, + nsIDOMMozSmsSegmentInfo** aResult) +{ + nsCOMPtr smsService = do_GetService(SMS_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE); + + return smsService->GetSegmentInfoForText(aText, aResult); +} + +nsresult +MobileMessageManager::Send(JSContext* aCx, JSObject* aGlobal, JSString* aNumber, + const nsAString& aMessage, jsval* aRequest) +{ + nsCOMPtr smsService = do_GetService(SMS_SERVICE_CONTRACTID); + if (!smsService) { + NS_ERROR("No SMS Service!"); + return NS_ERROR_FAILURE; + } + + nsCOMPtr request = SmsRequest::Create(this); + nsDependentJSString number; + number.init(aCx, aNumber); + + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(request.get())); + + smsService->Send(number, aMessage, forwarder); + + nsresult rv = nsContentUtils::WrapNative(aCx, aGlobal, request, aRequest); + if (NS_FAILED(rv)) { + NS_ERROR("Failed to create the js value!"); + return rv; + } + + return NS_OK; +} + +NS_IMETHODIMP +MobileMessageManager::Send(const jsval& aNumber, const nsAString& aMessage, jsval* aReturn) +{ + nsresult rv; + nsIScriptContext* sc = GetContextForEventHandlers(&rv); + NS_ENSURE_STATE(sc); + AutoPushJSContext cx(sc->GetNativeContext()); + NS_ASSERTION(cx, "Failed to get a context!"); + + if (!aNumber.isString() && + !(aNumber.isObject() && JS_IsArrayObject(cx, &aNumber.toObject()))) { + return NS_ERROR_INVALID_ARG; + } + + JSObject* global = sc->GetNativeGlobal(); + NS_ASSERTION(global, "Failed to get global object!"); + + JSAutoRequest ar(cx); + JSAutoCompartment ac(cx, global); + + if (aNumber.isString()) { + return Send(cx, global, aNumber.toString(), aMessage, aReturn); + } + + // Must be an array then. + JSObject& numbers = aNumber.toObject(); + + uint32_t size; + JS_ALWAYS_TRUE(JS_GetArrayLength(cx, &numbers, &size)); + + jsval* requests = new jsval[size]; + + for (uint32_t i=0; isetObjectOrNull(JS_NewArrayObject(cx, size, requests)); + NS_ENSURE_TRUE(aReturn->isObject(), NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP +MobileMessageManager::GetMessageMoz(int32_t aId, nsIDOMMozSmsRequest** aRequest) +{ + nsCOMPtr req = SmsRequest::Create(this); + nsCOMPtr mobileMessageDBService = + do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + mobileMessageDBService->GetMessageMoz(aId, forwarder); + req.forget(aRequest); + return NS_OK; +} + +nsresult +MobileMessageManager::Delete(int32_t aId, nsIDOMMozSmsRequest** aRequest) +{ + nsCOMPtr req = SmsRequest::Create(this); + nsCOMPtr mobileMessageDBService = + do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); + + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + mobileMessageDBService->DeleteMessage(aId, forwarder); + req.forget(aRequest); + return NS_OK; +} + +NS_IMETHODIMP +MobileMessageManager::Delete(const jsval& aParam, nsIDOMMozSmsRequest** aRequest) +{ + if (aParam.isInt32()) { + return Delete(aParam.toInt32(), aRequest); + } + + if (!aParam.isObject()) { + return NS_ERROR_INVALID_ARG; + } + + nsresult rv; + nsIScriptContext* sc = GetContextForEventHandlers(&rv); + AutoPushJSContext cx(sc->GetNativeContext()); + NS_ENSURE_STATE(sc); + nsCOMPtr message = + do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, &aParam.toObject())); + NS_ENSURE_TRUE(message, NS_ERROR_INVALID_ARG); + + int32_t id; + message->GetId(&id); + + return Delete(id, aRequest); +} + +NS_IMETHODIMP +MobileMessageManager::GetMessages(nsIDOMMozSmsFilter* aFilter, bool aReverse, + nsIDOMMozSmsRequest** aRequest) +{ + nsCOMPtr filter = aFilter; + + if (!filter) { + filter = new SmsFilter(); + } + + nsCOMPtr req = SmsRequest::Create(this); + nsCOMPtr mobileMessageDBService = + do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + mobileMessageDBService->CreateMessageList(filter, aReverse, forwarder); + req.forget(aRequest); + return NS_OK; +} + +NS_IMETHODIMP +MobileMessageManager::MarkMessageRead(int32_t aId, bool aValue, + nsIDOMMozSmsRequest** aRequest) +{ + nsCOMPtr req = SmsRequest::Create(this); + nsCOMPtr mobileMessageDBService = + do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); + mobileMessageDBService->MarkMessageRead(aId, aValue, forwarder); + req.forget(aRequest); + return NS_OK; +} + +NS_IMETHODIMP +MobileMessageManager::GetThreadList(nsIDOMMozSmsRequest** aRequest) +{ + nsCOMPtr req = SmsRequest::Create(this); + nsCOMPtr mobileMessageDBService = + do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); + mobileMessageDBService->GetThreadList(forwarder); + req.forget(aRequest); + return NS_OK; +} + +nsresult +MobileMessageManager::DispatchTrustedSmsEventToSelf(const nsAString& aEventName, + nsIDOMMozSmsMessage* aMessage) +{ + nsCOMPtr event; + NS_NewDOMMozSmsEvent(getter_AddRefs(event), this, nullptr, nullptr); + NS_ASSERTION(event, "This should never fail!"); + + nsCOMPtr se = do_QueryInterface(event); + MOZ_ASSERT(se); + nsresult rv = se->InitMozSmsEvent(aEventName, false, false, aMessage); + NS_ENSURE_SUCCESS(rv, rv); + + return DispatchTrustedEvent(event); +} + +NS_IMETHODIMP +MobileMessageManager::Observe(nsISupports* aSubject, const char* aTopic, + const PRUnichar* aData) +{ + if (!strcmp(aTopic, kSmsReceivedObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'sms-received' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedSmsEventToSelf(RECEIVED_EVENT_NAME, message); + return NS_OK; + } + + if (!strcmp(aTopic, kSmsSendingObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'sms-sending' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedSmsEventToSelf(SENDING_EVENT_NAME, message); + return NS_OK; + } + + if (!strcmp(aTopic, kSmsSentObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'sms-sent' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedSmsEventToSelf(SENT_EVENT_NAME, message); + return NS_OK; + } + + if (!strcmp(aTopic, kSmsFailedObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'sms-failed' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedSmsEventToSelf(FAILED_EVENT_NAME, message); + return NS_OK; + } + + if (!strcmp(aTopic, kSmsDeliverySuccessObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'sms-delivery-success' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedSmsEventToSelf(DELIVERY_SUCCESS_EVENT_NAME, message); + return NS_OK; + } + + if (!strcmp(aTopic, kSmsDeliveryErrorObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'sms-delivery-error' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedSmsEventToSelf(DELIVERY_ERROR_EVENT_NAME, message); + return NS_OK; + } + + return NS_OK; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/mobilemessage/src/MobileMessageManager.h b/dom/mobilemessage/src/MobileMessageManager.h new file mode 100644 index 00000000000..1a04f4fb447 --- /dev/null +++ b/dom/mobilemessage/src/MobileMessageManager.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 mozilla_dom_mobilemessage_MobileMessageManager_h +#define mozilla_dom_mobilemessage_MobileMessageManager_h + +#include "nsIDOMMobileMessageManager.h" +#include "nsIObserver.h" +#include "nsDOMEventTargetHelper.h" + +class nsIDOMMozSmsMessage; + +namespace mozilla { +namespace dom { + +class MobileMessageManager : public nsDOMEventTargetHelper + , public nsIDOMMozMobileMessageManager + , public nsIObserver +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOBSERVER + NS_DECL_NSIDOMMOZMOBILEMESSAGEMANAGER + + NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::) + + void Init(nsPIDOMWindow *aWindow); + void Shutdown(); + +private: + /** + * Internal Send() method used to send one message. + */ + nsresult Send(JSContext* aCx, JSObject* aGlobal, JSString* aNumber, + const nsAString& aMessage, jsval* aRequest); + + /** + * Internal Delete() method used to delete a message. + */ + nsresult Delete(int32_t aId, nsIDOMMozSmsRequest** aRequest); + + nsresult DispatchTrustedSmsEventToSelf(const nsAString& aEventName, + nsIDOMMozSmsMessage* aMessage); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_mobilemessage_MobileMessageManager_h diff --git a/dom/mobilemessage/src/SmsRequest.cpp b/dom/mobilemessage/src/SmsRequest.cpp index 9eb3da784ac..53c7b125cb8 100644 --- a/dom/mobilemessage/src/SmsRequest.cpp +++ b/dom/mobilemessage/src/SmsRequest.cpp @@ -14,6 +14,7 @@ #include "SmsCursor.h" #include "SmsMessage.h" #include "SmsManager.h" +#include "MobileMessageManager.h" #include "mozilla/dom/DOMError.h" #include "SmsParent.h" #include "jsapi.h" @@ -73,6 +74,13 @@ SmsRequest::Create(SmsManager* aManager) return request.forget(); } +already_AddRefed +SmsRequest::Create(MobileMessageManager* aManager) +{ + nsCOMPtr request = new SmsRequest(aManager); + return request.forget(); +} + already_AddRefed SmsRequest::Create(SmsRequestParent* aRequestParent) { @@ -90,6 +98,16 @@ SmsRequest::SmsRequest(SmsManager* aManager) BindToOwner(aManager); } +SmsRequest::SmsRequest(MobileMessageManager* aManager) + : mResult(JSVAL_VOID) + , mResultRooted(false) + , mDone(false) + , mParentAlive(false) + , mParent(nullptr) +{ + BindToOwner(aManager); +} + SmsRequest::SmsRequest(SmsRequestParent* aRequestParent) : mResult(JSVAL_VOID) , mResultRooted(false) diff --git a/dom/mobilemessage/src/SmsRequest.h b/dom/mobilemessage/src/SmsRequest.h index d6dc8fc0c31..175331bfb0c 100644 --- a/dom/mobilemessage/src/SmsRequest.h +++ b/dom/mobilemessage/src/SmsRequest.h @@ -49,6 +49,7 @@ private: }; class SmsManager; +class MobileMessageManager; class SmsRequest : public nsDOMEventTargetHelper , public nsIDOMMozSmsRequest @@ -68,6 +69,7 @@ public: nsDOMEventTargetHelper) static already_AddRefed Create(SmsManager* aManager); + static already_AddRefed Create(MobileMessageManager* aManager); static already_AddRefed Create(mobilemessage::SmsRequestParent* requestParent); void Reset(); @@ -83,6 +85,7 @@ private: SmsRequest() MOZ_DELETE; SmsRequest(SmsManager* aManager); + SmsRequest(MobileMessageManager* aManager); SmsRequest(mobilemessage::SmsRequestParent* aParent); ~SmsRequest(); diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index e0190966af3..98e64844e7d 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -334,6 +334,7 @@ var interfaceNamesInGlobalScope = "SVGAnimatedNumber", "HTMLHtmlElement", "MozSmsManager", + "MozMobileMessageManager", "MozSmsFilter", "SVGFETileElement", "MozMobileConnectionInfo", @@ -426,6 +427,7 @@ var interfaceNamesInGlobalScope = "MozSmsCursor", "EventSource", "MozNavigatorSms", + "MozNavigatorMobileMessage", "SVGSetElement", "GlobalObjectConstructor", "SVGAnimatedBoolean", From 3e9ced94a835b6f54ad706c74a4682d23a94c51c Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:21:55 +0800 Subject: [PATCH 038/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 2, nsIDOMMozMmsMessage). r=vicamo,mounir,mrbkap sr=sicking a=leo+ --- dom/base/nsDOMClassInfo.cpp | 8 + dom/base/nsDOMClassInfoClasses.h | 1 + dom/mobilemessage/interfaces/moz.build | 1 + .../interfaces/nsIDOMMozMmsMessage.idl | 43 ++ .../interfaces/nsIDOMMozSmsMessage.idl | 4 + dom/mobilemessage/src/Constants.h | 9 +- dom/mobilemessage/src/Makefile.in | 2 + dom/mobilemessage/src/MmsMessage.cpp | 411 ++++++++++++++++++ dom/mobilemessage/src/MmsMessage.h | 66 +++ dom/mobilemessage/src/Types.h | 9 +- .../mochitest/general/test_interfaces.html | 1 + embedding/android/GeckoSmsManager.java | 11 +- js/xpconnect/src/dictionary_helper_gen.conf | 3 +- mobile/android/base/GeckoSmsManager.java | 11 +- 14 files changed, 563 insertions(+), 17 deletions(-) create mode 100644 dom/mobilemessage/interfaces/nsIDOMMozMmsMessage.idl create mode 100644 dom/mobilemessage/src/MmsMessage.cpp create mode 100644 dom/mobilemessage/src/MmsMessage.h diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 022e89af2bc..fbee72c4f51 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -338,6 +338,7 @@ using mozilla::dom::workers::ResolveWorkerClasses; #include "nsIDOMSmsManager.h" #include "nsIDOMMobileMessageManager.h" #include "nsIDOMMozSmsMessage.h" +#include "nsIDOMMozMmsMessage.h" #include "nsIDOMSmsRequest.h" #include "nsIDOMSmsFilter.h" #include "nsIDOMSmsCursor.h" @@ -973,6 +974,9 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(MozSmsMessage, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(MozMmsMessage, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(MozSmsRequest, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -2532,6 +2536,10 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsMessage) DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(MozMmsMessage, nsIDOMMozMmsMessage) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozMmsMessage) + DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(MozSmsRequest, nsIDOMMozSmsRequest) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsRequest) DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index d0b122e49c7..4c28b29d217 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -207,6 +207,7 @@ DOMCI_CLASS(MozWakeLock) DOMCI_CLASS(MozSmsManager) DOMCI_CLASS(MozMobileMessageManager) DOMCI_CLASS(MozSmsMessage) +DOMCI_CLASS(MozMmsMessage) DOMCI_CLASS(MozSmsRequest) DOMCI_CLASS(MozSmsFilter) DOMCI_CLASS(MozSmsCursor) diff --git a/dom/mobilemessage/interfaces/moz.build b/dom/mobilemessage/interfaces/moz.build index 4bab624506c..a4885a48c09 100644 --- a/dom/mobilemessage/interfaces/moz.build +++ b/dom/mobilemessage/interfaces/moz.build @@ -5,6 +5,7 @@ XPIDL_SOURCES += [ 'nsIDOMMobileMessageManager.idl', + 'nsIDOMMozMmsMessage.idl', 'nsIDOMMozSmsEvent.idl', 'nsIDOMMozSmsMessage.idl', 'nsIDOMNavigatorMobileMessage.idl', diff --git a/dom/mobilemessage/interfaces/nsIDOMMozMmsMessage.idl b/dom/mobilemessage/interfaces/nsIDOMMozMmsMessage.idl new file mode 100644 index 00000000000..b5a5428d091 --- /dev/null +++ b/dom/mobilemessage/interfaces/nsIDOMMozMmsMessage.idl @@ -0,0 +1,43 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIDOMBlob; + +dictionary MmsAttachment +{ + DOMString? id; + DOMString? location; + nsIDOMBlob content; +}; + +[scriptable, builtinclass, uuid(df002ad2-71d7-11e2-9f1c-af6fa139069f)] +interface nsIDOMMozMmsMessage : nsISupports +{ + readonly attribute long id; + + /** + * Should be "not-downloaded", "received", "sending", "sent" or "error". + */ + readonly attribute DOMString state; + + [implicit_jscontext] + readonly attribute jsval deliveryStatus; // DOMString[] + + readonly attribute DOMString sender; + + [implicit_jscontext] + readonly attribute jsval receivers; // DOMString[] + + [implicit_jscontext] + readonly attribute jsval timestamp; // Date + + readonly attribute boolean read; + readonly attribute DOMString subject; + readonly attribute DOMString smil; + + [implicit_jscontext] + readonly attribute jsval attachments; // MmsAttachment[] +}; \ No newline at end of file diff --git a/dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl b/dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl index 9585e4a03f2..9ec5df029be 100644 --- a/dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl +++ b/dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl @@ -11,6 +11,10 @@ interface nsIDOMMozSmsMessage : nsISupports /** * Should be "received", "sending", "sent" or "error". + * + * TODO Bug 850530 Please see the IDL proposal at Bug 760065. + * We need to s/delivery/state in nsIDOMMozSmsMessage, which sounds + * a better name and can be consistent with nsIDOMMozMmsMessage. */ readonly attribute DOMString delivery; diff --git a/dom/mobilemessage/src/Constants.h b/dom/mobilemessage/src/Constants.h index 72322ada3ae..2acbff11fd7 100644 --- a/dom/mobilemessage/src/Constants.h +++ b/dom/mobilemessage/src/Constants.h @@ -18,10 +18,11 @@ extern const char* kSmsFailedObserverTopic; extern const char* kSmsDeliverySuccessObserverTopic; extern const char* kSmsDeliveryErrorObserverTopic; -#define DELIVERY_RECEIVED NS_LITERAL_STRING("received") -#define DELIVERY_SENDING NS_LITERAL_STRING("sending") -#define DELIVERY_SENT NS_LITERAL_STRING("sent") -#define DELIVERY_ERROR NS_LITERAL_STRING("error") +#define DELIVERY_RECEIVED NS_LITERAL_STRING("received") +#define DELIVERY_SENDING NS_LITERAL_STRING("sending") +#define DELIVERY_SENT NS_LITERAL_STRING("sent") +#define DELIVERY_ERROR NS_LITERAL_STRING("error") +#define DELIVERY_NOT_DOWNLOADED NS_LITERAL_STRING("not-downloaded") #define DELIVERY_STATUS_NOT_APPLICABLE NS_LITERAL_STRING("not-applicable") #define DELIVERY_STATUS_SUCCESS NS_LITERAL_STRING("success") diff --git a/dom/mobilemessage/src/Makefile.in b/dom/mobilemessage/src/Makefile.in index 9c3c554c35f..2253a556a25 100644 --- a/dom/mobilemessage/src/Makefile.in +++ b/dom/mobilemessage/src/Makefile.in @@ -38,6 +38,7 @@ EXPORTS_mozilla/dom = \ SmsManager.h \ MobileMessageManager.h \ SmsMessage.h \ + MmsMessage.h \ SmsRequest.h \ SmsSegmentInfo.h \ SmsFilter.h \ @@ -59,6 +60,7 @@ CPPSRCS = \ SmsServicesFactory.cpp \ SmsParent.cpp \ SmsMessage.cpp \ + MmsMessage.cpp \ Constants.cpp \ SmsChild.cpp \ SmsRequest.cpp \ diff --git a/dom/mobilemessage/src/MmsMessage.cpp b/dom/mobilemessage/src/MmsMessage.cpp new file mode 100644 index 00000000000..4c3d00c130d --- /dev/null +++ b/dom/mobilemessage/src/MmsMessage.cpp @@ -0,0 +1,411 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MmsMessage.h" +#include "nsIDOMClassInfo.h" +#include "jsapi.h" // For OBJECT_TO_JSVAL and JS_NewDateObjectMsec +#include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch +#include "nsJSUtils.h" +#include "Constants.h" +#include "nsContentUtils.h" +#include "nsIDOMFile.h" +#include "nsTArrayHelpers.h" + +using namespace mozilla::idl; +using namespace mozilla::dom::mobilemessage; + +DOMCI_DATA(MozMmsMessage, mozilla::dom::MmsMessage) + +namespace mozilla { +namespace dom { + +NS_INTERFACE_MAP_BEGIN(MmsMessage) + NS_INTERFACE_MAP_ENTRY(nsIDOMMozMmsMessage) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozMmsMessage) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(MmsMessage) +NS_IMPL_RELEASE(MmsMessage) + +MmsMessage::MmsMessage(int32_t aId, + DeliveryState aState, + const nsTArray& aDeliveryStatus, + const nsAString& aSender, + const nsTArray& aReceivers, + uint64_t aTimestamp, + bool aRead, + const nsAString& aSubject, + const nsAString& aSmil, + const nsTArray& aAttachments) + : mId(aId), + mState(aState), + mDeliveryStatus(aDeliveryStatus), + mSender(aSender), + mReceivers(aReceivers), + mTimestamp(aTimestamp), + mRead(aRead), + mSubject(aSubject), + mSmil(aSmil), + mAttachments(aAttachments) +{ +} + +/* static */ nsresult +MmsMessage::Create(int32_t aId, + const nsAString& aState, + const JS::Value& aDeliveryStatus, + const nsAString& aSender, + const JS::Value& aReceivers, + const JS::Value& aTimestamp, + bool aRead, + const nsAString& aSubject, + const nsAString& aSmil, + const JS::Value& aAttachments, + JSContext* aCx, + nsIDOMMozMmsMessage** aMessage) +{ + *aMessage = nullptr; + + // Set |state|. + DeliveryState state; + if (aState.Equals(DELIVERY_SENT)) { + state = eDeliveryState_Sent; + } else if (aState.Equals(DELIVERY_RECEIVED)) { + state = eDeliveryState_Received; + } else if (aState.Equals(DELIVERY_SENDING)) { + state = eDeliveryState_Sending; + } else if (aState.Equals(DELIVERY_NOT_DOWNLOADED)) { + state = eDeliveryState_NotDownloaded; + } else if (aState.Equals(DELIVERY_ERROR)) { + state = eDeliveryState_Error; + } else { + return NS_ERROR_INVALID_ARG; + } + + // Set |deliveryStatus|. + if (!aDeliveryStatus.isObject()) { + return NS_ERROR_INVALID_ARG; + } + JSObject* deliveryStatusObj = &aDeliveryStatus.toObject(); + if (!JS_IsArrayObject(aCx, deliveryStatusObj)) { + return NS_ERROR_INVALID_ARG; + } + + uint32_t length; + JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, deliveryStatusObj, &length)); + + nsTArray deliveryStatus; + for (uint32_t i = 0; i < length; ++i) { + JS::Value statusJsVal; + if (!JS_GetElement(aCx, deliveryStatusObj, i, &statusJsVal) || + !statusJsVal.isString()) { + return NS_ERROR_INVALID_ARG; + } + + nsDependentJSString statusStr; + statusStr.init(aCx, statusJsVal.toString()); + + DeliveryStatus status; + if (statusStr.Equals(DELIVERY_STATUS_NOT_APPLICABLE)) { + status = eDeliveryStatus_NotApplicable; + } else if (statusStr.Equals(DELIVERY_STATUS_SUCCESS)) { + status = eDeliveryStatus_Success; + } else if (statusStr.Equals(DELIVERY_STATUS_PENDING)) { + status = eDeliveryStatus_Pending; + } else if (statusStr.Equals(DELIVERY_STATUS_ERROR)) { + status = eDeliveryStatus_Error; + } else { + return NS_ERROR_INVALID_ARG; + } + + deliveryStatus.AppendElement(status); + } + + // Set |receivers|. + if (!aReceivers.isObject()) { + return NS_ERROR_INVALID_ARG; + } + JSObject* receiversObj = &aReceivers.toObject(); + if (!JS_IsArrayObject(aCx, receiversObj)) { + return NS_ERROR_INVALID_ARG; + } + + JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, receiversObj, &length)); + + nsTArray receivers; + for (uint32_t i = 0; i < length; ++i) { + JS::Value receiverJsVal; + if (!JS_GetElement(aCx, receiversObj, i, &receiverJsVal) || + !receiverJsVal.isString()) { + return NS_ERROR_INVALID_ARG; + } + + nsDependentJSString receiverStr; + receiverStr.init(aCx, receiverJsVal.toString()); + receivers.AppendElement(receiverStr); + } + + // Set |timestamp|. + uint64_t timestamp; + if (aTimestamp.isObject()) { + JSObject* timestampObj = &aTimestamp.toObject(); + if (!JS_ObjectIsDate(aCx, timestampObj)) { + return NS_ERROR_INVALID_ARG; + } + timestamp = js_DateGetMsecSinceEpoch(timestampObj); + } else { + if (!aTimestamp.isNumber()) { + return NS_ERROR_INVALID_ARG; + } + double number = aTimestamp.toNumber(); + if (static_cast(number) != number) { + return NS_ERROR_INVALID_ARG; + } + timestamp = static_cast(number); + } + + // Set |attachments|. + if (!aAttachments.isObject()) { + return NS_ERROR_INVALID_ARG; + } + JSObject* attachmentsObj = &aAttachments.toObject(); + if (!JS_IsArrayObject(aCx, attachmentsObj)) { + return NS_ERROR_INVALID_ARG; + } + + nsTArray attachments; + JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, attachmentsObj, &length)); + + for (uint32_t i = 0; i < length; ++i) { + JS::Value attachmentJsVal; + if (!JS_GetElement(aCx, attachmentsObj, i, &attachmentJsVal)) { + return NS_ERROR_INVALID_ARG; + } + + MmsAttachment attachment; + nsresult rv = attachment.Init(aCx, &attachmentJsVal); + NS_ENSURE_SUCCESS(rv, rv); + + attachments.AppendElement(attachment); + } + + nsCOMPtr message = new MmsMessage(aId, + state, + deliveryStatus, + aSender, + receivers, + timestamp, + aRead, + aSubject, + aSmil, + attachments); + message.forget(aMessage); + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetId(int32_t* aId) +{ + *aId = mId; + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetState(nsAString& aState) +{ + switch (mState) { + case eDeliveryState_Received: + aState = DELIVERY_RECEIVED; + break; + case eDeliveryState_Sending: + aState = DELIVERY_SENDING; + break; + case eDeliveryState_Sent: + aState = DELIVERY_SENT; + break; + case eDeliveryState_Error: + aState = DELIVERY_ERROR; + break; + case eDeliveryState_NotDownloaded: + aState = DELIVERY_NOT_DOWNLOADED; + break; + case eDeliveryState_Unknown: + case eDeliveryState_EndGuard: + default: + MOZ_NOT_REACHED("We shouldn't get any other delivery state!"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetDeliveryStatus(JSContext* aCx, JS::Value* aDeliveryStatus) +{ + // TODO Bug 850525 It would be better to depend on the state of MmsMessage + // to return a more correct value. Ex, when .state = 'received', we should + // also make .deliveryStatus = null, since the .deliveryStatus is useless. + uint32_t length = mDeliveryStatus.Length(); + if (length == 0) { + *aDeliveryStatus = JSVAL_NULL; + return NS_OK; + } + + nsTArray tempStrArray; + for (uint32_t i = 0; i < length; ++i) { + nsString statusStr; + switch (mDeliveryStatus[i]) { + case eDeliveryStatus_NotApplicable: + statusStr = DELIVERY_STATUS_NOT_APPLICABLE; + break; + case eDeliveryStatus_Success: + statusStr = DELIVERY_STATUS_SUCCESS; + break; + case eDeliveryStatus_Pending: + statusStr = DELIVERY_STATUS_PENDING; + break; + case eDeliveryStatus_Error: + statusStr = DELIVERY_STATUS_ERROR; + break; + case eDeliveryStatus_EndGuard: + default: + MOZ_NOT_REACHED("We shouldn't get any other delivery status!"); + return NS_ERROR_UNEXPECTED; + } + tempStrArray.AppendElement(statusStr); + } + + JSObject* deliveryStatusObj = nullptr; + nsresult rv = nsTArrayToJSArray(aCx, tempStrArray, &deliveryStatusObj); + NS_ENSURE_SUCCESS(rv, rv); + + aDeliveryStatus->setObject(*deliveryStatusObj); + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetSender(nsAString& aSender) +{ + aSender = mSender; + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetReceivers(JSContext* aCx, JS::Value* aReceivers) +{ + uint32_t length = mReceivers.Length(); + if (length == 0) { + return NS_ERROR_UNEXPECTED; + } + + JSObject* reveiversObj = nullptr; + nsresult rv = nsTArrayToJSArray(aCx, mReceivers, &reveiversObj); + NS_ENSURE_SUCCESS(rv, rv); + + aReceivers->setObject(*reveiversObj); + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetTimestamp(JSContext* cx, JS::Value* aDate) +{ + *aDate = OBJECT_TO_JSVAL(JS_NewDateObjectMsec(cx, mTimestamp)); + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetRead(bool* aRead) +{ + *aRead = mRead; + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetSubject(nsAString& aSubject) +{ + aSubject = mSubject; + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetSmil(nsAString& aSmil) +{ + aSmil = mSmil; + return NS_OK; +} + +NS_IMETHODIMP +MmsMessage::GetAttachments(JSContext* aCx, JS::Value* aAttachments) +{ + // TODO Bug 850529 We should return an empty array (or null) + // when it has no attachments? Need to further check this. + uint32_t length = mAttachments.Length(); + if (length == 0) { + *aAttachments = JSVAL_NULL; + return NS_OK; + } + + JSObject* attachments = JS_NewArrayObject(aCx, length, nullptr); + NS_ENSURE_TRUE(attachments, NS_ERROR_OUT_OF_MEMORY); + + for (uint32_t i = 0; i < length; ++i) { + const MmsAttachment &attachment = mAttachments[i]; + + JSObject* attachmentObj = JS_NewObject(aCx, nullptr, nullptr, nullptr); + NS_ENSURE_TRUE(attachmentObj, NS_ERROR_OUT_OF_MEMORY); + + JS::Value tmpJsVal; + JSString* tmpJsStr; + + // Get |attachment.mId|. + tmpJsStr = JS_NewUCStringCopyN(aCx, + attachment.id.get(), + attachment.id.Length()); + NS_ENSURE_TRUE(tmpJsStr, NS_ERROR_OUT_OF_MEMORY); + + tmpJsVal.setString(tmpJsStr); + if (!JS_DefineProperty(aCx, attachmentObj, "id", tmpJsVal, + NULL, NULL, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + + // Get |attachment.mLocation|. + tmpJsStr = JS_NewUCStringCopyN(aCx, + attachment.location.get(), + attachment.location.Length()); + NS_ENSURE_TRUE(tmpJsStr, NS_ERROR_OUT_OF_MEMORY); + + tmpJsVal.setString(tmpJsStr); + if (!JS_DefineProperty(aCx, attachmentObj, "location", tmpJsVal, + NULL, NULL, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + + // Get |attachment.mContent|. + nsresult rv = nsContentUtils::WrapNative(aCx, + JS_GetGlobalForScopeChain(aCx), + attachment.content, + &NS_GET_IID(nsIDOMBlob), + &tmpJsVal); + NS_ENSURE_SUCCESS(rv, rv); + + if (!JS_DefineProperty(aCx, attachmentObj, "content", tmpJsVal, + NULL, NULL, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + + tmpJsVal = OBJECT_TO_JSVAL(attachmentObj); + if (!JS_SetElement(aCx, attachments, i, &tmpJsVal)) { + return NS_ERROR_FAILURE; + } + } + + aAttachments->setObject(*attachments); + return NS_OK; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/mobilemessage/src/MmsMessage.h b/dom/mobilemessage/src/MmsMessage.h new file mode 100644 index 00000000000..6ed65a9414b --- /dev/null +++ b/dom/mobilemessage/src/MmsMessage.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 mozilla_dom_mobilemessage_MmsMessage_h +#define mozilla_dom_mobilemessage_MmsMessage_h + +#include "nsIDOMMozMmsMessage.h" +#include "nsString.h" +#include "jspubtd.h" +#include "mozilla/dom/mobilemessage/Types.h" +#include "mozilla/Attributes.h" +#include "DictionaryHelpers.h" + +namespace mozilla { +namespace dom { + +class MmsMessage MOZ_FINAL : public nsIDOMMozMmsMessage +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMMOZMMSMESSAGE + + MmsMessage(int32_t aId, + mobilemessage::DeliveryState aState, + const nsTArray& aDeliveryStatus, + const nsAString& aSender, + const nsTArray& aReceivers, + uint64_t aTimestamp, + bool aRead, + const nsAString& aSubject, + const nsAString& aSmil, + const nsTArray& aAttachments); + + static nsresult Create(int32_t aId, + const nsAString& aState, + const JS::Value& aDeliveryStatus, + const nsAString& aSender, + const JS::Value& aReceivers, + const JS::Value& aTimestamp, + bool aRead, + const nsAString& aSubject, + const nsAString& aSmil, + const JS::Value& aAttachments, + JSContext* aCx, + nsIDOMMozMmsMessage** aMessage); + +private: + + int32_t mId; + mobilemessage::DeliveryState mState; + nsTArray mDeliveryStatus; + nsString mSender; + nsTArray mReceivers; + uint64_t mTimestamp; + bool mRead; + nsString mSubject; + nsString mSmil; + nsTArray mAttachments; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_mobilemessage_MmsMessage_h diff --git a/dom/mobilemessage/src/Types.h b/dom/mobilemessage/src/Types.h index c85f883f0fc..92ea5b7bccc 100644 --- a/dom/mobilemessage/src/Types.h +++ b/dom/mobilemessage/src/Types.h @@ -12,7 +12,7 @@ namespace mozilla { namespace dom { namespace mobilemessage { -// For SmsMessageData.delivery. +// For MmsMessageData.state and SmsMessageData.deliveryState // Please keep the following files in sync with enum below: // embedding/android/GeckoSmsManager.java enum DeliveryState { @@ -21,11 +21,12 @@ enum DeliveryState { eDeliveryState_Sending, eDeliveryState_Error, eDeliveryState_Unknown, + eDeliveryState_NotDownloaded, // This state should stay at the end. eDeliveryState_EndGuard }; -// For SmsMessageData.deliveryStatus. +// For {Mms,Sms}MessageData.deliveryStatus. enum DeliveryStatus { eDeliveryStatus_NotApplicable = 0, eDeliveryStatus_Success, @@ -35,7 +36,7 @@ enum DeliveryStatus { eDeliveryStatus_EndGuard }; -// For SmsFilterData.read +// For {Mms,Sms}FilterData.read. enum ReadState { eReadState_Unknown = -1, eReadState_Unread, @@ -44,7 +45,7 @@ enum ReadState { eReadState_EndGuard }; -// For SmsFilterData.messageClass +// For {Mms,Sms}FilterData.messageClass. enum MessageClass { eMessageClass_Normal = 0, eMessageClass_Class0, diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 98e64844e7d..2bd14e09ea4 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -251,6 +251,7 @@ var interfaceNamesInGlobalScope = "SVGViewSpec", "DOMException", "MozSmsMessage", + "MozMmsMessage", "SVGFESpecularLightingElement", "StorageObsolete", "ContactManager", diff --git a/embedding/android/GeckoSmsManager.java b/embedding/android/GeckoSmsManager.java index 2f1cd224e74..7be4ee4fd8a 100644 --- a/embedding/android/GeckoSmsManager.java +++ b/embedding/android/GeckoSmsManager.java @@ -319,10 +319,13 @@ public class GeckoSmsManager * Keep the following state codes in syng with |DeliveryState| in: * dom/mobilemessage/src/Types.h */ - private final static int kDeliveryStateSent = 0; - private final static int kDeliveryStateReceived = 1; - private final static int kDeliveryStateUnknown = 2; - private final static int kDeliveryStateEndGuard = 3; + private final static int kDeliveryStateSent = 0; + private final static int kDeliveryStateReceived = 1; + private final static int kDeliveryStateSending = 2; + private final static int kDeliveryStateError = 3; + private final static int kDeliveryStateUnknown = 4; + private final static int kDeliveryStateNotDownloaded = 5; + private final static int kDeliveryStateEndGuard = 6; /* * Keep the following status codes in sync with |DeliveryStatus| in: diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index 9404c417ceb..daf1f8bf306 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -20,7 +20,8 @@ dictionaries = [ [ 'CameraSelector', 'nsIDOMCameraManager.idl' ], [ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ], [ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ], - [ 'SmsThreadListItem', 'nsISmsRequest.idl' ] + [ 'SmsThreadListItem', 'nsISmsRequest.idl' ], + [ 'MmsAttachment', 'nsIDOMMozMmsMessage.idl' ] ] # include file names diff --git a/mobile/android/base/GeckoSmsManager.java b/mobile/android/base/GeckoSmsManager.java index 4b2ec603cfb..756eaee379f 100644 --- a/mobile/android/base/GeckoSmsManager.java +++ b/mobile/android/base/GeckoSmsManager.java @@ -313,10 +313,13 @@ public class GeckoSmsManager * Keep the following state codes in syng with |DeliveryState| in: * dom/mobilemessage/src/Types.h */ - private final static int kDeliveryStateSent = 0; - private final static int kDeliveryStateReceived = 1; - private final static int kDeliveryStateUnknown = 2; - private final static int kDeliveryStateEndGuard = 3; + private final static int kDeliveryStateSent = 0; + private final static int kDeliveryStateReceived = 1; + private final static int kDeliveryStateSending = 2; + private final static int kDeliveryStateError = 3; + private final static int kDeliveryStateUnknown = 4; + private final static int kDeliveryStateNotDownloaded = 5; + private final static int kDeliveryStateEndGuard = 6; /* * Keep the following status codes in sync with |DeliveryStatus| in: From af36186707d2931e9713c0f78479022e9c1fbcbf Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:22:02 +0800 Subject: [PATCH 039/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 3-1, s/nsISmsRequest/nsIMobileMessageCallback). r=vicamo a=leo+ --HG-- rename : dom/mobilemessage/interfaces/nsISmsRequest.idl => dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl --- dom/mobilemessage/interfaces/moz.build | 2 +- ...quest.idl => nsIMobileMessageCallback.idl} | 4 +- .../nsIMobileMessageDatabaseService.idl | 30 +++++++++----- .../interfaces/nsISmsService.idl | 6 +-- .../src/MobileMessageManager.cpp | 15 ++++--- dom/mobilemessage/src/SmsCursor.cpp | 4 +- dom/mobilemessage/src/SmsCursor.h | 10 ++--- dom/mobilemessage/src/SmsManager.cpp | 15 ++++--- dom/mobilemessage/src/SmsRequest.cpp | 18 ++++----- dom/mobilemessage/src/SmsRequest.h | 16 ++++---- .../android/MobileMessageDatabaseService.cpp | 19 +++++---- dom/mobilemessage/src/android/SmsService.cpp | 2 +- .../fallback/MobileMessageDatabaseService.cpp | 12 +++--- dom/mobilemessage/src/fallback/SmsService.cpp | 2 +- dom/mobilemessage/src/ipc/SmsChild.cpp | 2 +- dom/mobilemessage/src/ipc/SmsChild.h | 6 +-- dom/mobilemessage/src/ipc/SmsIPCService.cpp | 16 ++++---- dom/mobilemessage/src/ipc/SmsParent.cpp | 14 +++---- .../src/ril/MobileMessageDatabaseService.js | 40 +++++++++---------- dom/mobilemessage/src/ril/SmsService.cpp | 2 +- dom/system/gonk/RadioInterfaceLayer.js | 4 +- dom/system/gonk/nsIRadioInterfaceLayer.idl | 6 +-- js/xpconnect/src/dictionary_helper_gen.conf | 2 +- widget/android/AndroidBridge.cpp | 18 +++++---- widget/android/AndroidBridge.h | 23 ++++++----- widget/android/AndroidJNI.cpp | 20 +++++----- 26 files changed, 168 insertions(+), 140 deletions(-) rename dom/mobilemessage/interfaces/{nsISmsRequest.idl => nsIMobileMessageCallback.idl} (93%) diff --git a/dom/mobilemessage/interfaces/moz.build b/dom/mobilemessage/interfaces/moz.build index a4885a48c09..c35809783ef 100644 --- a/dom/mobilemessage/interfaces/moz.build +++ b/dom/mobilemessage/interfaces/moz.build @@ -15,8 +15,8 @@ XPIDL_SOURCES += [ 'nsIDOMSmsManager.idl', 'nsIDOMSmsRequest.idl', 'nsIDOMSmsSegmentInfo.idl', + 'nsIMobileMessageCallback.idl', 'nsIMobileMessageDatabaseService.idl', - 'nsISmsRequest.idl', 'nsISmsService.idl', ] diff --git a/dom/mobilemessage/interfaces/nsISmsRequest.idl b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl similarity index 93% rename from dom/mobilemessage/interfaces/nsISmsRequest.idl rename to dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl index 993cb43ae2a..c8eb8191e7e 100644 --- a/dom/mobilemessage/interfaces/nsISmsRequest.idl +++ b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl @@ -14,8 +14,8 @@ dictionary SmsThreadListItem unsigned long long unreadCount; }; -[scriptable, builtinclass, uuid(82a6d16d-cf33-4745-8662-8b5d441f512f)] -interface nsISmsRequest : nsISupports +[scriptable, builtinclass, uuid(9b4f6e3e-8577-11e2-83c1-e3a75aac027d)] +interface nsIMobileMessageCallback : nsISupports { /** * All SMS related errors that could apply to SmsRequest objects. diff --git a/dom/mobilemessage/interfaces/nsIMobileMessageDatabaseService.idl b/dom/mobilemessage/interfaces/nsIMobileMessageDatabaseService.idl index 8ae36ab26ba..208edc74988 100644 --- a/dom/mobilemessage/interfaces/nsIMobileMessageDatabaseService.idl +++ b/dom/mobilemessage/interfaces/nsIMobileMessageDatabaseService.idl @@ -12,18 +12,30 @@ %} interface nsIDOMMozSmsFilter; -interface nsISmsRequest; +interface nsIMobileMessageCallback; -[scriptable, uuid(ce9232ca-6a08-11e2-b971-c795004622e7)] +[scriptable, uuid(4000064a-86d2-11e2-b4a6-ef0f30bd1e59)] interface nsIMobileMessageDatabaseService : nsISupports { [binaryname(GetMessageMoz)] - void getMessage(in long messageId, in nsISmsRequest request); - void deleteMessage(in long messageId, in nsISmsRequest request); - void createMessageList(in nsIDOMMozSmsFilter filter, in boolean reverse, in nsISmsRequest request); - void getNextMessageInList(in long listId, in nsISmsRequest request); - void clearMessageList(in long listId); - void markMessageRead(in long messageId, in boolean value, in nsISmsRequest request); + void getMessage(in long messageId, + in nsIMobileMessageCallback request); - void getThreadList(in nsISmsRequest request); + void deleteMessage(in long messageId, + in nsIMobileMessageCallback request); + + void createMessageList(in nsIDOMMozSmsFilter filter, + in boolean reverse, + in nsIMobileMessageCallback request); + + void getNextMessageInList(in long listId, + in nsIMobileMessageCallback request); + + void clearMessageList(in long listId); + + void markMessageRead(in long messageId, + in boolean value, + in nsIMobileMessageCallback request); + + void getThreadList(in nsIMobileMessageCallback request); }; diff --git a/dom/mobilemessage/interfaces/nsISmsService.idl b/dom/mobilemessage/interfaces/nsISmsService.idl index 00dbb1c56f0..7d0c6e2ba40 100644 --- a/dom/mobilemessage/interfaces/nsISmsService.idl +++ b/dom/mobilemessage/interfaces/nsISmsService.idl @@ -6,14 +6,14 @@ interface nsIDOMMozSmsMessage; interface nsIDOMMozSmsSegmentInfo; -interface nsISmsRequest; +interface nsIMobileMessageCallback; %{C++ #define SMS_SERVICE_CID { 0xbada3cb8, 0xa568, 0x4dff, { 0xb5, 0x43, 0x52, 0xbb, 0xb3, 0x14, 0x31, 0x21 } } #define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1" %} -[scriptable, builtinclass, uuid(4310bdb5-eefa-4f70-965a-74041228ab26)] +[scriptable, builtinclass, uuid(5d066568-86d2-11e2-a0d0-7351fb5ae50a)] interface nsISmsService : nsISupports { boolean hasSupport(); @@ -22,7 +22,7 @@ interface nsISmsService : nsISupports void send(in DOMString number, in DOMString message, - in nsISmsRequest request); + in nsIMobileMessageCallback request); [implicit_jscontext] nsIDOMMozSmsMessage createSmsMessage(in long id, diff --git a/dom/mobilemessage/src/MobileMessageManager.cpp b/dom/mobilemessage/src/MobileMessageManager.cpp index ebb21c410f9..2410b9b8a9b 100644 --- a/dom/mobilemessage/src/MobileMessageManager.cpp +++ b/dom/mobilemessage/src/MobileMessageManager.cpp @@ -111,7 +111,7 @@ MobileMessageManager::Send(JSContext* aCx, JSObject* aGlobal, JSString* aNumber, nsDependentJSString number; number.init(aCx, aNumber); - nsCOMPtr forwarder = + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(request.get())); smsService->Send(number, aMessage, forwarder); @@ -180,7 +180,8 @@ MobileMessageManager::GetMessageMoz(int32_t aId, nsIDOMMozSmsRequest** aRequest) nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->GetMessageMoz(aId, forwarder); req.forget(aRequest); return NS_OK; @@ -194,7 +195,8 @@ MobileMessageManager::Delete(int32_t aId, nsIDOMMozSmsRequest** aRequest) do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->DeleteMessage(aId, forwarder); req.forget(aRequest); return NS_OK; @@ -239,7 +241,8 @@ MobileMessageManager::GetMessages(nsIDOMMozSmsFilter* aFilter, bool aReverse, nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->CreateMessageList(filter, aReverse, forwarder); req.forget(aRequest); return NS_OK; @@ -253,7 +256,7 @@ MobileMessageManager::MarkMessageRead(int32_t aId, bool aValue, nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->MarkMessageRead(aId, aValue, forwarder); req.forget(aRequest); @@ -267,7 +270,7 @@ MobileMessageManager::GetThreadList(nsIDOMMozSmsRequest** aRequest) nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->GetThreadList(forwarder); req.forget(aRequest); diff --git a/dom/mobilemessage/src/SmsCursor.cpp b/dom/mobilemessage/src/SmsCursor.cpp index 8c3d4d0cb2f..5e2bb6a42c3 100644 --- a/dom/mobilemessage/src/SmsCursor.cpp +++ b/dom/mobilemessage/src/SmsCursor.cpp @@ -31,7 +31,7 @@ SmsCursor::SmsCursor() { } -SmsCursor::SmsCursor(int32_t aListId, nsISmsRequest* aRequest) +SmsCursor::SmsCursor(int32_t aListId, nsIMobileMessageCallback* aRequest) : mListId(aListId) , mRequest(aRequest) { @@ -86,7 +86,7 @@ SmsCursor::Continue() do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = new SmsRequestForwarder(request); + nsCOMPtr forwarder = new SmsRequestForwarder(request); mobileMessageDBService->GetNextMessageInList(mListId, forwarder); // We intenionally increase the refcount. The release will be called diff --git a/dom/mobilemessage/src/SmsCursor.h b/dom/mobilemessage/src/SmsCursor.h index cea046c1158..7c18d08b826 100644 --- a/dom/mobilemessage/src/SmsCursor.h +++ b/dom/mobilemessage/src/SmsCursor.h @@ -12,7 +12,7 @@ #include "mozilla/Attributes.h" class nsIDOMMozSmsMessage; -class nsISmsRequest; +class nsIMobileMessageCallback; namespace mozilla { namespace dom { @@ -26,7 +26,7 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS(SmsCursor) SmsCursor(); - SmsCursor(int32_t aListId, nsISmsRequest* aRequest); + SmsCursor(int32_t aListId, nsIMobileMessageCallback* aRequest); ~SmsCursor(); @@ -35,9 +35,9 @@ public: void Disconnect(); private: - int32_t mListId; - nsCOMPtr mRequest; - nsCOMPtr mMessage; + int32_t mListId; + nsCOMPtr mRequest; + nsCOMPtr mMessage; }; inline void diff --git a/dom/mobilemessage/src/SmsManager.cpp b/dom/mobilemessage/src/SmsManager.cpp index 629fc7ae648..29c03d99817 100644 --- a/dom/mobilemessage/src/SmsManager.cpp +++ b/dom/mobilemessage/src/SmsManager.cpp @@ -152,7 +152,7 @@ SmsManager::Send(JSContext* aCx, JSObject* aGlobal, JSString* aNumber, nsDependentJSString number; number.init(aCx, aNumber); - nsCOMPtr forwarder = + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(request.get())); smsService->Send(number, aMessage, forwarder); @@ -221,7 +221,8 @@ SmsManager::GetMessageMoz(int32_t aId, nsIDOMMozSmsRequest** aRequest) nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->GetMessageMoz(aId, forwarder); req.forget(aRequest); return NS_OK; @@ -235,7 +236,8 @@ SmsManager::Delete(int32_t aId, nsIDOMMozSmsRequest** aRequest) do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->DeleteMessage(aId, forwarder); req.forget(aRequest); return NS_OK; @@ -280,7 +282,8 @@ SmsManager::GetMessages(nsIDOMMozSmsFilter* aFilter, bool aReverse, nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); + nsCOMPtr forwarder = + new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->CreateMessageList(filter, aReverse, forwarder); req.forget(aRequest); return NS_OK; @@ -294,7 +297,7 @@ SmsManager::MarkMessageRead(int32_t aId, bool aValue, nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->MarkMessageRead(aId, aValue, forwarder); req.forget(aRequest); @@ -308,7 +311,7 @@ SmsManager::GetThreadList(nsIDOMMozSmsRequest** aRequest) nsCOMPtr mobileMessageDBService = do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE); - nsCOMPtr forwarder = + nsCOMPtr forwarder = new SmsRequestForwarder(static_cast(req.get())); mobileMessageDBService->GetThreadList(forwarder); req.forget(aRequest); diff --git a/dom/mobilemessage/src/SmsRequest.cpp b/dom/mobilemessage/src/SmsRequest.cpp index 53c7b125cb8..528fb099d9d 100644 --- a/dom/mobilemessage/src/SmsRequest.cpp +++ b/dom/mobilemessage/src/SmsRequest.cpp @@ -31,7 +31,7 @@ DOMCI_DATA(MozSmsRequest, mozilla::dom::SmsRequest) namespace mozilla { namespace dom { -NS_IMPL_ISUPPORTS1(SmsRequestForwarder, nsISmsRequest) +NS_IMPL_ISUPPORTS1(SmsRequestForwarder, nsIMobileMessageCallback) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SmsRequest, nsDOMEventTargetHelper) @@ -57,7 +57,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SmsRequest) NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsRequest) NS_INTERFACE_MAP_ENTRY(nsIDOMDOMRequest) - NS_INTERFACE_MAP_ENTRY(nsISmsRequest) + NS_INTERFACE_MAP_ENTRY(nsIMobileMessageCallback) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozSmsRequest) NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) @@ -205,7 +205,7 @@ SmsRequest::SetSuccessInternal(nsISupports* aObject) nsresult rv; nsIScriptContext* sc = GetContextForEventHandlers(&rv); if (!sc) { - SetError(nsISmsRequest::INTERNAL_ERROR); + SetError(nsIMobileMessageCallback::INTERNAL_ERROR); return false; } @@ -222,7 +222,7 @@ SmsRequest::SetSuccessInternal(nsISupports* aObject) if (NS_FAILED(nsContentUtils::WrapNative(cx, global, aObject, &mResult))) { UnrootResult(); - SetError(nsISmsRequest::INTERNAL_ERROR); + SetError(nsIMobileMessageCallback::INTERNAL_ERROR); return false; } @@ -236,23 +236,23 @@ SmsRequest::SetError(int32_t aError) NS_PRECONDITION(!mDone, "mDone shouldn't have been set to true already!"); NS_PRECONDITION(!mError, "mError shouldn't have been set!"); NS_PRECONDITION(mResult == JSVAL_VOID, "mResult shouldn't have been set!"); - NS_PRECONDITION(aError != nsISmsRequest::SUCCESS_NO_ERROR, + NS_PRECONDITION(aError != nsIMobileMessageCallback::SUCCESS_NO_ERROR, "Can't call SetError() with SUCCESS_NO_ERROR!"); mDone = true; mCursor = nullptr; switch (aError) { - case nsISmsRequest::NO_SIGNAL_ERROR: + case nsIMobileMessageCallback::NO_SIGNAL_ERROR: mError = DOMError::CreateWithName(NS_LITERAL_STRING("NoSignalError")); break; - case nsISmsRequest::NOT_FOUND_ERROR: + case nsIMobileMessageCallback::NOT_FOUND_ERROR: mError = DOMError::CreateWithName(NS_LITERAL_STRING("NotFoundError")); break; - case nsISmsRequest::UNKNOWN_ERROR: + case nsIMobileMessageCallback::UNKNOWN_ERROR: mError = DOMError::CreateWithName(NS_LITERAL_STRING("UnknownError")); break; - case nsISmsRequest::INTERNAL_ERROR: + case nsIMobileMessageCallback::INTERNAL_ERROR: mError = DOMError::CreateWithName(NS_LITERAL_STRING("InternalError")); break; default: // SUCCESS_NO_ERROR is handled above. diff --git a/dom/mobilemessage/src/SmsRequest.h b/dom/mobilemessage/src/SmsRequest.h index 175331bfb0c..776c70d8a2f 100644 --- a/dom/mobilemessage/src/SmsRequest.h +++ b/dom/mobilemessage/src/SmsRequest.h @@ -7,7 +7,7 @@ #define mozilla_dom_mobilemessage_SmsRequest_h #include "nsIDOMSmsRequest.h" -#include "nsISmsRequest.h" +#include "nsIMobileMessageCallback.h" #include "nsDOMEventTargetHelper.h" class nsIDOMMozSmsMessage; @@ -25,15 +25,15 @@ namespace mobilemessage { // We need this forwarder to avoid a QI to nsIClassInfo. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=775997#c51 -class SmsRequestForwarder : public nsISmsRequest +class SmsRequestForwarder : public nsIMobileMessageCallback { friend class mobilemessage::SmsRequestChild; public: NS_DECL_ISUPPORTS - NS_FORWARD_NSISMSREQUEST(mRealRequest->) + NS_FORWARD_NSIMOBILEMESSAGECALLBACK(mRealRequest->) - SmsRequestForwarder(nsISmsRequest* aRealRequest) { + SmsRequestForwarder(nsIMobileMessageCallback* aRealRequest) { mRealRequest = aRealRequest; } @@ -41,11 +41,11 @@ private: virtual ~SmsRequestForwarder() {} - nsISmsRequest* GetRealRequest() { + nsIMobileMessageCallback* GetRealRequest() { return mRealRequest; } - nsCOMPtr mRealRequest; + nsCOMPtr mRealRequest; }; class SmsManager; @@ -53,14 +53,14 @@ class MobileMessageManager; class SmsRequest : public nsDOMEventTargetHelper , public nsIDOMMozSmsRequest - , public nsISmsRequest + , public nsIMobileMessageCallback { public: friend class SmsCursor; NS_DECL_ISUPPORTS NS_DECL_NSIDOMDOMREQUEST - NS_DECL_NSISMSREQUEST + NS_DECL_NSIMOBILEMESSAGECALLBACK NS_DECL_NSIDOMMOZSMSREQUEST NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::) diff --git a/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp b/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp index f7968c0c08e..e7adc25b3b0 100644 --- a/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp +++ b/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp @@ -14,7 +14,8 @@ namespace mobilemessage { NS_IMPL_ISUPPORTS1(MobileMessageDatabaseService, nsIMobileMessageDatabaseService) NS_IMETHODIMP -MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId, nsISmsRequest* aRequest) +MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId, + nsIMobileMessageCallback* aRequest) { if (!AndroidBridge::Bridge()) { return NS_OK; @@ -25,7 +26,8 @@ MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId, nsISmsRequest* a } NS_IMETHODIMP -MobileMessageDatabaseService::DeleteMessage(int32_t aMessageId, nsISmsRequest* aRequest) +MobileMessageDatabaseService::DeleteMessage(int32_t aMessageId, + nsIMobileMessageCallback* aRequest) { if (!AndroidBridge::Bridge()) { return NS_OK; @@ -37,7 +39,8 @@ MobileMessageDatabaseService::DeleteMessage(int32_t aMessageId, nsISmsRequest* a NS_IMETHODIMP MobileMessageDatabaseService::CreateMessageList(nsIDOMMozSmsFilter* aFilter, - bool aReverse, nsISmsRequest* aRequest) + bool aReverse, + nsIMobileMessageCallback* aRequest) { if (!AndroidBridge::Bridge()) { return NS_OK; @@ -49,7 +52,8 @@ MobileMessageDatabaseService::CreateMessageList(nsIDOMMozSmsFilter* aFilter, } NS_IMETHODIMP -MobileMessageDatabaseService::GetNextMessageInList(int32_t aListId, nsISmsRequest* aRequest) +MobileMessageDatabaseService::GetNextMessageInList(int32_t aListId, + nsIMobileMessageCallback* aRequest) { if (!AndroidBridge::Bridge()) { return NS_OK; @@ -71,15 +75,16 @@ MobileMessageDatabaseService::ClearMessageList(int32_t aListId) } NS_IMETHODIMP -MobileMessageDatabaseService::MarkMessageRead(int32_t aMessageId, bool aValue, - nsISmsRequest* aRequest) +MobileMessageDatabaseService::MarkMessageRead(int32_t aMessageId, + bool aValue, + nsIMobileMessageCallback* aRequest) { // TODO: This would need to be implemented as part of Bug 748391 return NS_OK; } NS_IMETHODIMP -MobileMessageDatabaseService::GetThreadList(nsISmsRequest* aRequest) +MobileMessageDatabaseService::GetThreadList(nsIMobileMessageCallback* aRequest) { NS_NOTYETIMPLEMENTED("Implement me!"); return NS_ERROR_NOT_IMPLEMENTED; diff --git a/dom/mobilemessage/src/android/SmsService.cpp b/dom/mobilemessage/src/android/SmsService.cpp index 394293bb1a6..15da95f0bdb 100644 --- a/dom/mobilemessage/src/android/SmsService.cpp +++ b/dom/mobilemessage/src/android/SmsService.cpp @@ -41,7 +41,7 @@ SmsService::GetSegmentInfoForText(const nsAString & aText, NS_IMETHODIMP SmsService::Send(const nsAString& aNumber, const nsAString& aMessage, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { if (!AndroidBridge::Bridge()) { return NS_OK; diff --git a/dom/mobilemessage/src/fallback/MobileMessageDatabaseService.cpp b/dom/mobilemessage/src/fallback/MobileMessageDatabaseService.cpp index 8123f6f08fb..6911d351d18 100644 --- a/dom/mobilemessage/src/fallback/MobileMessageDatabaseService.cpp +++ b/dom/mobilemessage/src/fallback/MobileMessageDatabaseService.cpp @@ -13,7 +13,7 @@ NS_IMPL_ISUPPORTS1(MobileMessageDatabaseService, nsIMobileMessageDatabaseService NS_IMETHODIMP MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { NS_ERROR("We should not be here!"); return NS_OK; @@ -21,7 +21,7 @@ MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId, NS_IMETHODIMP MobileMessageDatabaseService::DeleteMessage(int32_t aMessageId, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { NS_ERROR("We should not be here!"); return NS_OK; @@ -30,7 +30,7 @@ MobileMessageDatabaseService::DeleteMessage(int32_t aMessageId, NS_IMETHODIMP MobileMessageDatabaseService::CreateMessageList(nsIDOMMozSmsFilter* aFilter, bool aReverse, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { NS_ERROR("We should not be here!"); return NS_OK; @@ -38,7 +38,7 @@ MobileMessageDatabaseService::CreateMessageList(nsIDOMMozSmsFilter* aFilter, NS_IMETHODIMP MobileMessageDatabaseService::GetNextMessageInList(int32_t aListId, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { NS_ERROR("We should not be here!"); return NS_OK; @@ -54,14 +54,14 @@ MobileMessageDatabaseService::ClearMessageList(int32_t aListId) NS_IMETHODIMP MobileMessageDatabaseService::MarkMessageRead(int32_t aMessageId, bool aValue, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { NS_ERROR("We should not be here!"); return NS_OK; } NS_IMETHODIMP -MobileMessageDatabaseService::GetThreadList(nsISmsRequest* aRequest) +MobileMessageDatabaseService::GetThreadList(nsIMobileMessageCallback* aRequest) { NS_ERROR("We should not be here!"); return NS_OK; diff --git a/dom/mobilemessage/src/fallback/SmsService.cpp b/dom/mobilemessage/src/fallback/SmsService.cpp index 3cf66dff606..2b9c09d31f7 100644 --- a/dom/mobilemessage/src/fallback/SmsService.cpp +++ b/dom/mobilemessage/src/fallback/SmsService.cpp @@ -32,7 +32,7 @@ SmsService::GetSegmentInfoForText(const nsAString & aText, NS_IMETHODIMP SmsService::Send(const nsAString& aNumber, const nsAString& aMessage, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { NS_ERROR("We should not be here!"); return NS_OK; diff --git a/dom/mobilemessage/src/ipc/SmsChild.cpp b/dom/mobilemessage/src/ipc/SmsChild.cpp index 5665c344068..6bce70cd242 100644 --- a/dom/mobilemessage/src/ipc/SmsChild.cpp +++ b/dom/mobilemessage/src/ipc/SmsChild.cpp @@ -110,7 +110,7 @@ SmsChild::DeallocPSmsRequest(PSmsRequestChild* aActor) * SmsRequestChild ******************************************************************************/ -SmsRequestChild::SmsRequestChild(nsISmsRequest* aReplyRequest) +SmsRequestChild::SmsRequestChild(nsIMobileMessageCallback* aReplyRequest) : mReplyRequest(aReplyRequest) { MOZ_COUNT_CTOR(SmsRequestChild); diff --git a/dom/mobilemessage/src/ipc/SmsChild.h b/dom/mobilemessage/src/ipc/SmsChild.h index 85b27de60fc..7e31c9bfdeb 100644 --- a/dom/mobilemessage/src/ipc/SmsChild.h +++ b/dom/mobilemessage/src/ipc/SmsChild.h @@ -9,7 +9,7 @@ #include "mozilla/dom/mobilemessage/PSmsChild.h" #include "mozilla/dom/mobilemessage/PSmsRequestChild.h" -class nsISmsRequest; +class nsIMobileMessageCallback; namespace mozilla { namespace dom { @@ -55,10 +55,10 @@ class SmsRequestChild : public PSmsRequestChild { friend class SmsChild; - nsCOMPtr mReplyRequest; + nsCOMPtr mReplyRequest; public: - SmsRequestChild(nsISmsRequest* aReplyRequest); + SmsRequestChild(nsIMobileMessageCallback* aReplyRequest); protected: virtual ~SmsRequestChild(); diff --git a/dom/mobilemessage/src/ipc/SmsIPCService.cpp b/dom/mobilemessage/src/ipc/SmsIPCService.cpp index 097ed63fb42..1a232af3523 100644 --- a/dom/mobilemessage/src/ipc/SmsIPCService.cpp +++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp @@ -22,7 +22,7 @@ PSmsChild* gSmsChild; NS_IMPL_ISUPPORTS2(SmsIPCService, nsISmsService, nsIMobileMessageDatabaseService) void -SendRequest(const IPCSmsRequest& aRequest, nsISmsRequest* aRequestReply) +SendRequest(const IPCSmsRequest& aRequest, nsIMobileMessageCallback* aRequestReply) { MOZ_ASSERT(NS_IsMainThread()); @@ -73,7 +73,7 @@ SmsIPCService::GetSegmentInfoForText(const nsAString & aText, NS_IMETHODIMP SmsIPCService::Send(const nsAString& aNumber, const nsAString& aMessage, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { SendRequest(SendMessageRequest(nsString(aNumber), nsString(aMessage)), aRequest); return NS_OK; @@ -115,7 +115,7 @@ SmsIPCService::CreateSmsSegmentInfo(int32_t aSegments, */ NS_IMETHODIMP SmsIPCService::GetMessageMoz(int32_t aMessageId, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { SendRequest(GetMessageRequest(aMessageId), aRequest); return NS_OK; @@ -123,7 +123,7 @@ SmsIPCService::GetMessageMoz(int32_t aMessageId, NS_IMETHODIMP SmsIPCService::DeleteMessage(int32_t aMessageId, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { SendRequest(DeleteMessageRequest(aMessageId), aRequest); return NS_OK; @@ -132,7 +132,7 @@ SmsIPCService::DeleteMessage(int32_t aMessageId, NS_IMETHODIMP SmsIPCService::CreateMessageList(nsIDOMMozSmsFilter* aFilter, bool aReverse, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { SmsFilterData data = SmsFilterData(static_cast(aFilter)->GetData()); SendRequest(CreateMessageListRequest(data, aReverse), aRequest); @@ -141,7 +141,7 @@ SmsIPCService::CreateMessageList(nsIDOMMozSmsFilter* aFilter, NS_IMETHODIMP SmsIPCService::GetNextMessageInList(int32_t aListId, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { SendRequest(GetNextMessageInListRequest(aListId), aRequest); return NS_OK; @@ -157,14 +157,14 @@ SmsIPCService::ClearMessageList(int32_t aListId) NS_IMETHODIMP SmsIPCService::MarkMessageRead(int32_t aMessageId, bool aValue, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { SendRequest(MarkMessageReadRequest(aMessageId, aValue), aRequest); return NS_OK; } NS_IMETHODIMP -SmsIPCService::GetThreadList(nsISmsRequest* aRequest) +SmsIPCService::GetThreadList(nsIMobileMessageCallback* aRequest) { SendRequest(GetThreadListRequest(), aRequest); return NS_OK; diff --git a/dom/mobilemessage/src/ipc/SmsParent.cpp b/dom/mobilemessage/src/ipc/SmsParent.cpp index bddb3a6cc8b..191dac46f67 100644 --- a/dom/mobilemessage/src/ipc/SmsParent.cpp +++ b/dom/mobilemessage/src/ipc/SmsParent.cpp @@ -266,7 +266,7 @@ SmsRequestParent::DoRequest(const SendMessageRequest& aRequest) NS_ENSURE_TRUE(smsService, true); mSmsRequest = SmsRequest::Create(this); - nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); + nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); nsresult rv = smsService->Send(aRequest.number(), aRequest.message(), forwarder); NS_ENSURE_SUCCESS(rv, false); @@ -281,7 +281,7 @@ SmsRequestParent::DoRequest(const GetMessageRequest& aRequest) NS_ENSURE_TRUE(mobileMessageDBService, true); mSmsRequest = SmsRequest::Create(this); - nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); + nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); nsresult rv = mobileMessageDBService->GetMessageMoz(aRequest.messageId(), forwarder); NS_ENSURE_SUCCESS(rv, false); @@ -296,7 +296,7 @@ SmsRequestParent::DoRequest(const DeleteMessageRequest& aRequest) NS_ENSURE_TRUE(mobileMessageDBService, true); mSmsRequest = SmsRequest::Create(this); - nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); + nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); nsresult rv = mobileMessageDBService->DeleteMessage(aRequest.messageId(), forwarder); NS_ENSURE_SUCCESS(rv, false); @@ -311,7 +311,7 @@ SmsRequestParent::DoRequest(const CreateMessageListRequest& aRequest) NS_ENSURE_TRUE(mobileMessageDBService, true); mSmsRequest = SmsRequest::Create(this); - nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); + nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); SmsFilter *filter = new SmsFilter(aRequest.filter()); nsresult rv = mobileMessageDBService->CreateMessageList(filter, aRequest.reverse(), forwarder); NS_ENSURE_SUCCESS(rv, false); @@ -327,7 +327,7 @@ SmsRequestParent::DoRequest(const GetNextMessageInListRequest& aRequest) NS_ENSURE_TRUE(mobileMessageDBService, true); mSmsRequest = SmsRequest::Create(this); - nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); + nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); nsresult rv = mobileMessageDBService->GetNextMessageInList(aRequest.aListId(), forwarder); NS_ENSURE_SUCCESS(rv, false); @@ -342,7 +342,7 @@ SmsRequestParent::DoRequest(const MarkMessageReadRequest& aRequest) NS_ENSURE_TRUE(mobileMessageDBService, true); mSmsRequest = SmsRequest::Create(this); - nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); + nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); nsresult rv = mobileMessageDBService->MarkMessageRead(aRequest.messageId(), aRequest.value(), forwarder); NS_ENSURE_SUCCESS(rv, false); @@ -357,7 +357,7 @@ SmsRequestParent::DoRequest(const GetThreadListRequest& aRequest) NS_ENSURE_TRUE(mobileMessageDBService, true); mSmsRequest = SmsRequest::Create(this); - nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); + nsCOMPtr forwarder = new SmsRequestForwarder(mSmsRequest); nsresult rv = mobileMessageDBService->GetThreadList(forwarder); NS_ENSURE_SUCCESS(rv, false); diff --git a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js index f889187f6d8..fc96d9745c9 100644 --- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js +++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js @@ -619,7 +619,7 @@ MobileMessageDatabaseService.prototype = { // An previous error found. Keep the answer in results so that we can // reply INTERNAL_ERROR for further requests. if (DEBUG) debug("An previous error found"); - smsRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + smsRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); return; } @@ -652,7 +652,7 @@ MobileMessageDatabaseService.prototype = { debug("notifyReadMessageListFailed - listId: " + aMessageList.listId + ", messageId: " + firstMessageId); } - smsRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + smsRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); }; }, @@ -1219,7 +1219,7 @@ MobileMessageDatabaseService.prototype = { this.newTxn(READ_ONLY, function (error, txn, messageStore) { if (error) { if (DEBUG) debug(error); - aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); return; } let request = messageStore.mozGetAll(messageId); @@ -1228,13 +1228,13 @@ MobileMessageDatabaseService.prototype = { if (DEBUG) debug("Transaction " + txn + " completed."); if (request.result.length > 1) { if (DEBUG) debug("Got too many results for id " + messageId); - aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.UNKNOWN_ERROR); + aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR); return; } let messageRecord = request.result[0]; if (!messageRecord) { if (DEBUG) debug("Message ID " + messageId + " not found"); - aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR); + aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR); return; } if (messageRecord.id != messageId) { @@ -1242,7 +1242,7 @@ MobileMessageDatabaseService.prototype = { debug("Requested message ID (" + messageId + ") is " + "different from the one we got"); } - aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.UNKNOWN_ERROR); + aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR); return; } let sms = self.createSmsMessageFromRecord(messageRecord); @@ -1255,7 +1255,7 @@ MobileMessageDatabaseService.prototype = { debug("Caught error on transaction", event.target.errorCode); } //TODO look at event.target.errorCode, pick appropriate error constant - aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); }; }); }, @@ -1265,13 +1265,13 @@ MobileMessageDatabaseService.prototype = { let self = this; this.newTxn(READ_WRITE, function (error, txn, stores) { if (error) { - aRequest.notifyDeleteMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyDeleteMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); return; } txn.onerror = function onerror(event) { if (DEBUG) debug("Caught error on transaction", event.target.errorCode); //TODO look at event.target.errorCode, pick appropriate error constant - aRequest.notifyDeleteMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyDeleteMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); }; const messageStore = stores[0]; @@ -1363,7 +1363,7 @@ MobileMessageDatabaseService.prototype = { if (error) { //TODO look at event.target.errorCode, pick appropriate error constant. if (DEBUG) debug("IDBRequest error " + error.target.errorCode); - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); return; } @@ -1652,7 +1652,7 @@ MobileMessageDatabaseService.prototype = { let list = this.messageLists[listId]; if (!list) { if (DEBUG) debug("Wrong list id"); - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR); + aRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR); return; } if (list.processing) { @@ -1671,7 +1671,7 @@ MobileMessageDatabaseService.prototype = { return; } if (list.results[0] < 0) { - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); return; } messageId = list.results.shift(); @@ -1688,7 +1688,7 @@ MobileMessageDatabaseService.prototype = { if (DEBUG) debug("Transaction " + txn + " completed."); if (!messageRecord) { if (DEBUG) debug("Could not get message id " + messageId); - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR); + aRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR); } let sms = self.createSmsMessageFromRecord(messageRecord); aRequest.notifyNextMessageInListGot(sms); @@ -1700,7 +1700,7 @@ MobileMessageDatabaseService.prototype = { debug("Error retrieving message id: " + messageId + ". Error code: " + event.target.errorCode); } - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); }; }); }, @@ -1718,12 +1718,12 @@ MobileMessageDatabaseService.prototype = { this.newTxn(READ_WRITE, function (error, txn, stores) { if (error) { if (DEBUG) debug(error); - aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); return; } txn.onerror = function onerror(event) { if (DEBUG) debug("Caught error on transaction ", event.target.errorCode); - aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); }; let messageStore = stores[0]; let threadStore = stores[1]; @@ -1731,7 +1731,7 @@ MobileMessageDatabaseService.prototype = { let messageRecord = event.target.result; if (!messageRecord) { if (DEBUG) debug("Message ID " + messageId + " not found"); - aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR); + aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR); return; } if (messageRecord.id != messageId) { @@ -1739,7 +1739,7 @@ MobileMessageDatabaseService.prototype = { debug("Retrieve message ID (" + messageId + ") is " + "different from the one we got"); } - aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.UNKNOWN_ERROR); + aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR); return; } // If the value to be set is the same as the current message `read` @@ -1785,12 +1785,12 @@ MobileMessageDatabaseService.prototype = { this.newTxn(READ_ONLY, function (error, txn, threadStore) { if (error) { if (DEBUG) debug(error); - aRequest.notifyThreadListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyThreadListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); return; } txn.onerror = function onerror(event) { if (DEBUG) debug("Caught error on transaction ", event.target.errorCode); - aRequest.notifyThreadListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + aRequest.notifyThreadListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); }; let request = threadStore.index("lastTimestamp").mozGetAll(); request.onsuccess = function(event) { diff --git a/dom/mobilemessage/src/ril/SmsService.cpp b/dom/mobilemessage/src/ril/SmsService.cpp index 9ccaa38839e..c84c6ac177f 100644 --- a/dom/mobilemessage/src/ril/SmsService.cpp +++ b/dom/mobilemessage/src/ril/SmsService.cpp @@ -39,7 +39,7 @@ SmsService::GetSegmentInfoForText(const nsAString & aText, NS_IMETHODIMP SmsService::Send(const nsAString& aNumber, const nsAString& aMessage, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { if (!mRIL) { return NS_OK; diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index c39ca98254a..3cc21522b7a 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -1673,10 +1673,10 @@ RadioInterfaceLayer.prototype = { } delete this._sentSmsEnvelopes[message.envelopeId]; - let error = Ci.nsISmsRequest.UNKNOWN_ERROR; + let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR; switch (message.errorMsg) { case RIL.ERROR_RADIO_NOT_AVAILABLE: - error = Ci.nsISmsRequest.NO_SIGNAL_ERROR; + error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR; break; } diff --git a/dom/system/gonk/nsIRadioInterfaceLayer.idl b/dom/system/gonk/nsIRadioInterfaceLayer.idl index da46b5c989c..59eceddb3d9 100644 --- a/dom/system/gonk/nsIRadioInterfaceLayer.idl +++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl @@ -6,7 +6,7 @@ interface nsIDOMMozMobileConnectionInfo; interface nsIDOMMozMobileICCInfo; -interface nsISmsRequest; +interface nsIMobileMessageCallback; interface nsIDOMMozSmsSegmentInfo; [scriptable, uuid(1e602d20-d066-4399-8997-daf36b3158ef)] @@ -108,7 +108,7 @@ interface nsIRilContext : nsISupports readonly attribute nsIDOMMozMobileConnectionInfo data; }; -[scriptable, uuid(e5f8cfa9-fd23-4c8d-b06b-635fdbae8b90)] +[scriptable, uuid(8b3a1bc8-86d2-11e2-ace0-33f0ed290b90)] interface nsIRadioInterfaceLayer : nsISupports { /** @@ -148,7 +148,7 @@ interface nsIRadioInterfaceLayer : nsISupports void sendSMS(in DOMString number, in DOMString message, - in nsISmsRequest request); + in nsIMobileMessageCallback request); /** * ICC-related functionality. diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index daf1f8bf306..2d38d84d1cf 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -20,7 +20,7 @@ dictionaries = [ [ 'CameraSelector', 'nsIDOMCameraManager.idl' ], [ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ], [ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ], - [ 'SmsThreadListItem', 'nsISmsRequest.idl' ], + [ 'SmsThreadListItem', 'nsIMobileMessageCallback.idl' ], [ 'MmsAttachment', 'nsIDOMMozMmsMessage.idl' ] ] diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index df88ba6bec3..5e596e223c9 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -60,7 +60,7 @@ class AndroidRefable { // This isn't in AndroidBridge.h because including StrongPointer.h there is gross static android::sp (*android_SurfaceTexture_getNativeWindow)(JNIEnv* env, jobject surfaceTexture) = nullptr; -/* static */ StaticAutoPtr > > AndroidBridge::sSmsRequests; +/* static */ StaticAutoPtr > > AndroidBridge::sSmsRequests; void AndroidBridge::ConstructBridge(JNIEnv *jEnv, @@ -1670,7 +1670,9 @@ AndroidBridge::GetSegmentInfoForText(const nsAString& aText, } void -AndroidBridge::SendMessage(const nsAString& aNumber, const nsAString& aMessage, nsISmsRequest* aRequest) +AndroidBridge::SendMessage(const nsAString& aNumber, + const nsAString& aMessage, + nsIMobileMessageCallback* aRequest) { ALOG_BRIDGE("AndroidBridge::SendMessage"); @@ -1690,7 +1692,7 @@ AndroidBridge::SendMessage(const nsAString& aNumber, const nsAString& aMessage, } void -AndroidBridge::GetMessage(int32_t aMessageId, nsISmsRequest* aRequest) +AndroidBridge::GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest) { ALOG_BRIDGE("AndroidBridge::GetMessage"); @@ -1707,7 +1709,7 @@ AndroidBridge::GetMessage(int32_t aMessageId, nsISmsRequest* aRequest) } void -AndroidBridge::DeleteMessage(int32_t aMessageId, nsISmsRequest* aRequest) +AndroidBridge::DeleteMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest) { ALOG_BRIDGE("AndroidBridge::DeleteMessage"); @@ -1725,7 +1727,7 @@ AndroidBridge::DeleteMessage(int32_t aMessageId, nsISmsRequest* aRequest) void AndroidBridge::CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, bool aReverse, - nsISmsRequest* aRequest) + nsIMobileMessageCallback* aRequest) { ALOG_BRIDGE("AndroidBridge::CreateMessageList"); @@ -1756,7 +1758,7 @@ AndroidBridge::CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilte } void -AndroidBridge::GetNextMessageInList(int32_t aListId, nsISmsRequest* aRequest) +AndroidBridge::GetNextMessageInList(int32_t aListId, nsIMobileMessageCallback* aRequest) { ALOG_BRIDGE("AndroidBridge::GetNextMessageInList"); @@ -1786,7 +1788,7 @@ AndroidBridge::ClearMessageList(int32_t aListId) } bool -AndroidBridge::QueueSmsRequest(nsISmsRequest* aRequest, uint32_t* aRequestIdOut) +AndroidBridge::QueueSmsRequest(nsIMobileMessageCallback* aRequest, uint32_t* aRequestIdOut) { MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); MOZ_ASSERT(aRequest && aRequestIdOut); @@ -1812,7 +1814,7 @@ AndroidBridge::QueueSmsRequest(nsISmsRequest* aRequest, uint32_t* aRequestIdOut) return true; } -already_AddRefed +already_AddRefed AndroidBridge::DequeueSmsRequest(uint32_t aRequestId) { MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 12b16b53942..58499942af0 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -25,7 +25,7 @@ #include "gfxRect.h" #include "nsIAndroidBridge.h" -#include "nsISmsRequest.h" +#include "nsIMobileMessageCallback.h" #include "mozilla/Likely.h" #include "mozilla/StaticPtr.h" @@ -314,14 +314,17 @@ public: void DisableBatteryNotifications(); void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo); - nsresult GetSegmentInfoForText(const nsAString& aText, dom::mobilemessage::SmsSegmentInfoData* aData); - void SendMessage(const nsAString& aNumber, const nsAString& aText, nsISmsRequest* aRequest); - void GetMessage(int32_t aMessageId, nsISmsRequest* aRequest); - void DeleteMessage(int32_t aMessageId, nsISmsRequest* aRequest); - void CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, bool aReverse, nsISmsRequest* aRequest); - void GetNextMessageInList(int32_t aListId, nsISmsRequest* aRequest); + nsresult GetSegmentInfoForText(const nsAString& aText, + dom::mobilemessage::SmsSegmentInfoData* aData); + void SendMessage(const nsAString& aNumber, const nsAString& aText, + nsIMobileMessageCallback* aRequest); + void GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest); + void DeleteMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest); + void CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, + bool aReverse, nsIMobileMessageCallback* aRequest); + void GetNextMessageInList(int32_t aListId, nsIMobileMessageCallback* aRequest); void ClearMessageList(int32_t aListId); - already_AddRefed DequeueSmsRequest(uint32_t aRequestId); + already_AddRefed DequeueSmsRequest(uint32_t aRequestId); bool IsTablet(); @@ -367,7 +370,7 @@ public: nsACString & aResult); protected: static AndroidBridge *sBridge; - static StaticAutoPtr > > sSmsRequests; + static StaticAutoPtr > > sSmsRequests; // the global JavaVM JavaVM *mJavaVM; @@ -398,7 +401,7 @@ protected: int mAPIVersion; - bool QueueSmsRequest(nsISmsRequest* aRequest, uint32_t* aRequestIdOut); + bool QueueSmsRequest(nsIMobileMessageCallback* aRequest, uint32_t* aRequestIdOut); // other things jmethodID jNotifyIME; diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp index b25c753fca6..eedd56446b9 100644 --- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -245,7 +245,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifySmsSent(JNIEnv* jenv, jclass, nsCOMPtr message = new SmsMessage(mMessageData); obs->NotifyObservers(message, kSmsSentObserverTopic, nullptr); - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -326,7 +326,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifySmsSendFailed(JNIEnv* jenv, jclass, {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -364,7 +364,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifyGetSms(JNIEnv* jenv, jclass, {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -408,7 +408,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifyGetSmsFailed(JNIEnv* jenv, jclass, {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -440,7 +440,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDeleted(JNIEnv* jenv, jclass, {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -473,7 +473,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDeleteFailed(JNIEnv* jenv, jclas {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -503,7 +503,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifyNoMessageInList(JNIEnv* jenv, jclas {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -543,7 +543,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifyListCreated(JNIEnv* jenv, jclass, {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -595,7 +595,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifyGotNextMessage(JNIEnv* jenv, jclass {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); @@ -641,7 +641,7 @@ Java_org_mozilla_gecko_GeckoSmsManager_notifyReadingMessageListFailed(JNIEnv* je {} NS_IMETHODIMP Run() { - nsCOMPtr request = + nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); From 72cae1a57dba9fd1b33e0cc9edb1dfd19cee9172 Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:22:10 +0800 Subject: [PATCH 040/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 3-2, nsIMobileMessageCallback.notifyMessageSent()). r=vicamo a=leo+ --- .../interfaces/nsIMobileMessageCallback.idl | 7 +- dom/mobilemessage/src/Makefile.in | 2 + .../src/MobileMessageCallback.cpp | 161 ++++++++++++++++++ dom/mobilemessage/src/MobileMessageCallback.h | 37 ++++ dom/mobilemessage/src/SmsRequest.cpp | 14 +- 5 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 dom/mobilemessage/src/MobileMessageCallback.cpp create mode 100644 dom/mobilemessage/src/MobileMessageCallback.h diff --git a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl index c8eb8191e7e..99ff437c36c 100644 --- a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl +++ b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl @@ -14,7 +14,7 @@ dictionary SmsThreadListItem unsigned long long unreadCount; }; -[scriptable, builtinclass, uuid(9b4f6e3e-8577-11e2-83c1-e3a75aac027d)] +[scriptable, builtinclass, uuid(18d7c4da-848f-11e2-a3a5-f749c4ba31b5)] interface nsIMobileMessageCallback : nsISupports { /** @@ -28,7 +28,10 @@ interface nsIMobileMessageCallback : nsISupports const unsigned short UNKNOWN_ERROR = 3; const unsigned short INTERNAL_ERROR = 4; - void notifyMessageSent(in nsIDOMMozSmsMessage message); + /** + * |message| can be either |nsIDOMMozSmsMessage| or |nsIDOMMozMmsMessage|. + */ + void notifyMessageSent(in nsISupports message); void notifySendMessageFailed(in long error); void notifyMessageGot(in nsIDOMMozSmsMessage message); diff --git a/dom/mobilemessage/src/Makefile.in b/dom/mobilemessage/src/Makefile.in index 2253a556a25..9de9d9bacab 100644 --- a/dom/mobilemessage/src/Makefile.in +++ b/dom/mobilemessage/src/Makefile.in @@ -50,6 +50,7 @@ EXPORTS_mozilla/dom/mobilemessage = \ SmsServicesFactory.h \ Constants.h \ Types.h \ + MobileMessageCallback.h \ $(NULL) CPPSRCS = \ @@ -64,6 +65,7 @@ CPPSRCS = \ Constants.cpp \ SmsChild.cpp \ SmsRequest.cpp \ + MobileMessageCallback.cpp \ SmsFilter.cpp \ SmsSegmentInfo.cpp \ SmsCursor.cpp \ diff --git a/dom/mobilemessage/src/MobileMessageCallback.cpp b/dom/mobilemessage/src/MobileMessageCallback.cpp new file mode 100644 index 00000000000..01279eba57a --- /dev/null +++ b/dom/mobilemessage/src/MobileMessageCallback.cpp @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MobileMessageCallback.h" +#include "nsContentUtils.h" +#include "nsIDOMMozSmsMessage.h" +#include "nsIDOMMozMmsMessage.h" +#include "nsIScriptGlobalObject.h" +#include "nsPIDOMWindow.h" +#include "MmsMessage.h" +#include "jsapi.h" +#include "xpcpublic.h" +#include "nsServiceManagerUtils.h" + +namespace mozilla { +namespace dom { +namespace mobilemessage { + +NS_IMPL_ADDREF(MobileMessageCallback) +NS_IMPL_RELEASE(MobileMessageCallback) + +NS_INTERFACE_MAP_BEGIN(MobileMessageCallback) + NS_INTERFACE_MAP_ENTRY(nsIMobileMessageCallback) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +MobileMessageCallback::MobileMessageCallback(DOMRequest* aDOMRequest) + : mDOMRequest(aDOMRequest) +{ +} + +MobileMessageCallback::~MobileMessageCallback() +{ +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyMessageSent(nsISupports *aMessage) +{ + nsresult rv; + nsIScriptContext* scriptContext = mDOMRequest->GetContextForEventHandlers(&rv); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE); + + AutoPushJSContext cx(scriptContext->GetNativeContext()); + NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); + + jsval wrappedMessage; + rv = nsContentUtils::WrapNative(cx, + JS_GetGlobalObject(cx), + aMessage, + &wrappedMessage); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE); + + rs->FireSuccess(mDOMRequest, wrappedMessage); + + return NS_OK; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifySendMessageFailed(int32_t aError) +{ + nsCOMPtr rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE); + + switch (aError) { + case nsIMobileMessageCallback::NO_SIGNAL_ERROR: + return rs->FireError(mDOMRequest, NS_LITERAL_STRING("NoSignalError")); + case nsIMobileMessageCallback::NOT_FOUND_ERROR: + return rs->FireError(mDOMRequest, NS_LITERAL_STRING("NotFoundError")); + case nsIMobileMessageCallback::UNKNOWN_ERROR: + return rs->FireError(mDOMRequest, NS_LITERAL_STRING("UnknownError")); + case nsIMobileMessageCallback::INTERNAL_ERROR: + return rs->FireError(mDOMRequest, NS_LITERAL_STRING("InternalError")); + default: // SUCCESS_NO_ERROR is handled above. + MOZ_ASSERT(false, "Unknown error value."); + } + + return NS_OK; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyMessageGot(nsIDOMMozSmsMessage *aMessage) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyGetMessageFailed(int32_t aError) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyMessageDeleted(bool aDeleted) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyDeleteMessageFailed(int32_t aError) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyMessageListCreated(int32_t aListId, + nsIDOMMozSmsMessage *aMessage) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyReadMessageListFailed(int32_t aError) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyNextMessageInListGot(nsIDOMMozSmsMessage *aMessage) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyNoMessageInList() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyMessageMarkedRead(bool aRead) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyMarkMessageReadFailed(int32_t aError) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyThreadList(const jsval& aThreadList, JSContext* aCx) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +MobileMessageCallback::NotifyThreadListFailed(int32_t aError) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +} // namesapce mobilemessage +} // namespace dom +} // namespace mozilla diff --git a/dom/mobilemessage/src/MobileMessageCallback.h b/dom/mobilemessage/src/MobileMessageCallback.h new file mode 100644 index 00000000000..6bff8a9937c --- /dev/null +++ b/dom/mobilemessage/src/MobileMessageCallback.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 mozilla_dom_mobilemessage_MobileMessageCallback_h +#define mozilla_dom_mobilemessage_MobileMessageCallback_h + +#include "nsIMobileMessageCallback.h" +#include "nsCOMPtr.h" +#include "DOMRequest.h" + +class nsIDOMMozMmsMessage; + +namespace mozilla { +namespace dom { +namespace mobilemessage { + +class MobileMessageCallback MOZ_FINAL : public nsIMobileMessageCallback +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMOBILEMESSAGECALLBACK + + MobileMessageCallback(DOMRequest* aDOMRequest); + +private: + ~MobileMessageCallback(); + + nsRefPtr mDOMRequest; +}; + +} // namespace mobilemessage +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_mobilemessage_MobileMessageCallback_h diff --git a/dom/mobilemessage/src/SmsRequest.cpp b/dom/mobilemessage/src/SmsRequest.cpp index 528fb099d9d..d44b4832ef3 100644 --- a/dom/mobilemessage/src/SmsRequest.cpp +++ b/dom/mobilemessage/src/SmsRequest.cpp @@ -342,13 +342,21 @@ SmsRequest::SendMessageReply(const MessageReply& aReply) } NS_IMETHODIMP -SmsRequest::NotifyMessageSent(nsIDOMMozSmsMessage *aMessage) +SmsRequest::NotifyMessageSent(nsISupports *aMessage) { + // We only support nsIDOMMozSmsMessage for SmsRequest. + nsCOMPtr message(do_QueryInterface(aMessage)); + if (!message) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + SmsMessage* smsMessage = static_cast(message.get()); + if (mParent) { - SmsMessageData data = SmsMessageData(static_cast(aMessage)->GetData()); + SmsMessageData data = SmsMessageData(smsMessage->GetData()); return SendMessageReply(MessageReply(ReplyMessageSend(data))); } - return NotifySuccess(aMessage); + return NotifySuccess(smsMessage); } NS_IMETHODIMP From a85a3117cf814fd188ec1680e781d2ce0b2f3fcc Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:22:17 +0800 Subject: [PATCH 041/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 4-1, multiple delivery status). r=vicamo a=leo+ --- .../nsIRilMobileMessageDatabaseService.idl | 15 ++- .../src/ril/MobileMessageDatabaseService.js | 94 ++++++++++++++++--- dom/system/gonk/RadioInterfaceLayer.js | 11 +-- 3 files changed, 98 insertions(+), 22 deletions(-) diff --git a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl index 609447ddcc4..22be04b0795 100644 --- a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl +++ b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl @@ -15,7 +15,7 @@ interface nsIRilMobileMessageDatabaseCallback : nsISupports void notify(in nsresult aRv, in jsval aRecord); }; -[scriptable, uuid(3592525a-71d6-11e2-82ca-f75ae6e08ee2)] +[scriptable, uuid(a31b1716-8631-11e2-afaa-2fbd087f426e)] interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService { /** @@ -31,14 +31,23 @@ interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService /** * |aMessage| Object: should contain the following properties for internal use: * - |type| DOMString: "sms" or "mms" - * - |receiver| DOMString: the phone number of receiver + * - |receiver| DOMString: the phone number of receiver (needed if type = "sms") + * - |receivers| DOMString: the phone numbers of receivers (needed if type = "mms") * - |timestamp| Number: the timestamp of sending message - * - |deliveryStatus| DOMString: the delivery status of sending message + * - |deliveryStatusRequested| Bool: true when the delivery report is requested. */ long saveSendingMessage(in jsval aMessage, [optional] in nsIRilMobileMessageDatabaseCallback aCallback); + /** + * |aMessageId| Number: the message's DB record ID. + * |aReceiver| DOMString: the phone number of receiver (for MMS; can be null). + * |aDelivery| DOMString: the new delivery value to update (can be null). + * |aDeliveryStatus| DOMString: the new delivery status to update (can be null). + * |aCallback| nsIRilMobileMessageDatabaseCallback: an optional callback. + */ void setMessageDelivery(in long aMessageId, + in DOMString aReceiver, in DOMString aDelivery, in DOMString aDeliveryStatus, [optional] in nsIRilMobileMessageDatabaseCallback aCallback); diff --git a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js index fc96d9745c9..f7d8d11ec95 100644 --- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js +++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js @@ -1120,16 +1120,41 @@ MobileMessageDatabaseService.prototype = { }, saveSendingMessage: function saveSendingMessage(aMessage, aCallback) { - if (aMessage.type === undefined || - aMessage.receiver === undefined || - aMessage.deliveryStatus === undefined || - aMessage.timestamp === undefined) { + if ((aMessage.type != "sms" && aMessage.type != "mms") || + (aMessage.type == "sms" && !aMessage.receiver) || + (aMessage.type == "mms" && !aMessage.receivers) || + aMessage.deliveryStatusRequested == undefined || + aMessage.timestamp == undefined) { if (aCallback) { aCallback.notify(Cr.NS_ERROR_FAILURE, null); } return; } + // Set |aMessage.deliveryStatus|. Note that for MMS record + // it must be an array of strings; For SMS, it's a string. + let deliveryStatus = aMessage.deliveryStatusRequested + ? DELIVERY_STATUS_PENDING + : DELIVERY_STATUS_NOT_APPLICABLE; + if (aMessage.type == "sms") { + aMessage.deliveryStatus = deliveryStatus; + } else if (aMessage.type == "mms") { + let receivers = aMessage.receivers + if (!Array.isArray(receivers)) { + if (DEBUG) { + debug("Need receivers for MMS. Fail to save the sending message."); + } + if (aCallback) { + aCallback.notify(Cr.NS_ERROR_FAILURE, null); + } + return; + } + aMessage.deliveryStatus = []; + for (let i = 0; i < receivers.length; i++) { + aMessage.deliveryStatus.push(deliveryStatus); + } + } + aMessage.sender = this.getRilIccInfoMsisdn(); let timestamp = aMessage.timestamp; @@ -1141,11 +1166,17 @@ MobileMessageDatabaseService.prototype = { aMessage.messageClass = MESSAGE_CLASS_NORMAL; aMessage.read = FILTER_READ_READ; - return this.saveRecord(aMessage, [aMessage.receiver], aCallback); + let addresses; + if (aMessage.type == "sms") { + addresses = [aMessage.receiver]; + } else if (aMessage.type == "mms") { + addresses = aMessage.receivers; + } + return this.saveRecord(aMessage, addresses, aCallback); }, setMessageDelivery: function setMessageDelivery( - messageId, delivery, deliveryStatus, callback) { + messageId, receiver, delivery, deliveryStatus, callback) { if (DEBUG) { debug("Setting message " + messageId + " delivery to " + delivery + ", and deliveryStatus to " + deliveryStatus); @@ -1188,21 +1219,58 @@ MobileMessageDatabaseService.prototype = { } return; } + + let isRecordUpdated = false; + + // Update |messageRecord.delivery| if needed. + if (delivery && messageRecord.delivery != delivery) { + messageRecord.delivery = delivery; + messageRecord.deliveryIndex = [delivery, messageRecord.timestamp]; + isRecordUpdated = true; + } + + // Update |messageRecord.deliveryStatus| if needed. + if (deliveryStatus) { + if (messageRecord.type == "sms") { + if (messageRecord.deliveryStatus != deliveryStatus) { + messageRecord.deliveryStatus = deliveryStatus; + isRecordUpdated = true; + } + } else if (messageRecord.type == "mms") { + if (!receiver) { + for (let i = 0; i < messageRecord.deliveryStatus.length; i++) { + if (messageRecord.deliveryStatus[i] != deliveryStatus) { + messageRecord.deliveryStatus[i] = deliveryStatus; + isRecordUpdated = true; + } + } + } else { + let index = messageRecord.receivers.indexOf(receiver); + if (index == -1) { + if (DEBUG) { + debug("Cannot find the receiver. Fail to set delivery status."); + } + return; + } + if (messageRecord.deliveryStatus[index] != deliveryStatus) { + messageRecord.deliveryStatus[index] = deliveryStatus; + isRecordUpdated = true; + } + } + } + } + // Only updates messages that have different delivery or deliveryStatus. - if ((messageRecord.delivery == delivery) - && (messageRecord.deliveryStatus == deliveryStatus)) { + if (!isRecordUpdated) { if (DEBUG) { debug("The values of attribute delivery and deliveryStatus are the" + " the same with given parameters."); } return; } - messageRecord.delivery = delivery; - messageRecord.deliveryIndex = [delivery, messageRecord.timestamp]; - messageRecord.deliveryStatus = deliveryStatus; + if (DEBUG) { - debug("Message.delivery set to: " + delivery - + ", and Message.deliveryStatus set to: " + deliveryStatus); + debug("The record's delivery and/or deliveryStatus are updated."); } messageStore.put(messageRecord); }; diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 3cc21522b7a..d0dd528fbc0 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -1613,6 +1613,7 @@ RadioInterfaceLayer.prototype = { } gMobileMessageDatabaseService.setMessageDelivery(options.sms.id, + null, DOM_MOBILE_MESSAGE_DELIVERY_SENT, options.sms.deliveryStatus, function notifyResult(rv, record) { @@ -1652,6 +1653,7 @@ RadioInterfaceLayer.prototype = { delete this._sentSmsEnvelopes[message.envelopeId]; gMobileMessageDatabaseService.setMessageDelivery(options.sms.id, + null, options.sms.delivery, message.deliveryStatus, function notifyResult(rv, record) { @@ -1681,6 +1683,7 @@ RadioInterfaceLayer.prototype = { } gMobileMessageDatabaseService.setMessageDelivery(options.sms.id, + null, DOM_MOBILE_MESSAGE_DELIVERY_ERROR, RIL.GECKO_SMS_DELIVERY_STATUS_ERROR, function notifyResult(rv, record) { @@ -2677,16 +2680,12 @@ RadioInterfaceLayer.prototype = { options.segmentRef = this.nextSegmentRef; } - let timestamp = Date.now(); - let deliveryStatus = options.requestStatusReport - ? RIL.GECKO_SMS_DELIVERY_STATUS_PENDING - : RIL.GECKO_SMS_DELIVERY_STATUS_NOT_APPLICABLE; let sendingMessage = { type: "sms", receiver: number, body: message, - deliveryStatus: deliveryStatus, - timestamp: timestamp + deliveryStatusRequested: options.requestStatusReport, + timestamp: Date.now() }; let id = gMobileMessageDatabaseService.saveSendingMessage(sendingMessage, From 9fe417700a6d6cae95818d28c598fbb9fcb7adce Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:22:25 +0800 Subject: [PATCH 042/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 4-2, nsIMmsService.send()). r=vicamo a=leo+ --- dom/mms/interfaces/nsIMmsService.idl | 7 +- dom/mms/src/ril/MmsService.js | 158 +++++++++++++++++- dom/mobilemessage/interfaces/moz.build | 1 + .../interfaces/nsIDOMMobileMessageManager.idl | 8 + .../interfaces/nsIMobileMessageService.idl | 45 +++++ .../interfaces/nsISmsService.idl | 17 +- dom/mobilemessage/src/Makefile.in | 2 + .../src/MobileMessageService.cpp | 99 +++++++++++ dom/mobilemessage/src/MobileMessageService.h | 33 ++++ dom/mobilemessage/src/android/SmsService.cpp | 31 ---- dom/mobilemessage/src/fallback/SmsService.cpp | 31 ---- dom/mobilemessage/src/ipc/SmsIPCService.cpp | 31 ---- .../src/ril/MobileMessageDatabaseService.js | 24 +-- dom/mobilemessage/src/ril/SmsService.cpp | 31 ---- .../tests/test_smsservice_createsmsmessage.js | 6 +- dom/system/gonk/RadioInterfaceLayer.js | 30 ++-- js/xpconnect/src/dictionary_helper_gen.conf | 3 +- layout/build/nsLayoutModule.cpp | 13 +- 18 files changed, 393 insertions(+), 177 deletions(-) create mode 100644 dom/mobilemessage/interfaces/nsIMobileMessageService.idl create mode 100644 dom/mobilemessage/src/MobileMessageService.cpp create mode 100644 dom/mobilemessage/src/MobileMessageService.h diff --git a/dom/mms/interfaces/nsIMmsService.idl b/dom/mms/interfaces/nsIMmsService.idl index 45d46bed655..b8cb8c463f2 100644 --- a/dom/mms/interfaces/nsIMmsService.idl +++ b/dom/mms/interfaces/nsIMmsService.idl @@ -3,9 +3,12 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" +interface nsIMobileMessageCallback; +interface nsIDOMBlob; -[scriptable, uuid(217ddd76-75db-4210-955d-8806cd8d87f9)] +[scriptable, uuid(3ec33286-8559-11e2-9f38-e76b58650568)] interface nsIMmsService : nsISupports { - boolean hasSupport(); + void send(in jsval parameters /* MmsParameters */, + in nsIMobileMessageCallback request); }; diff --git a/dom/mms/src/ril/MmsService.js b/dom/mms/src/ril/MmsService.js index 8c1efd4d318..0e52dfb857b 100644 --- a/dom/mms/src/ril/MmsService.js +++ b/dom/mms/src/ril/MmsService.js @@ -66,6 +66,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService", "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1", "nsIRilMobileMessageDatabaseService"); +XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService", + "@mozilla.org/mobilemessage/mobilemessageservice;1", + "nsIMobileMessageService"); + XPCOMUtils.defineLazyGetter(this, "MMS", function () { let MMS = {}; Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS); @@ -1099,10 +1103,160 @@ MmsService.prototype = { debug("handleDeliveryIndication: got delivery report for " + messageId); }, + createSavableFromParams: function createSavableFromParams(aParams) { + debug("createSavableFromParams: aParams: " + JSON.stringify(aParams)); + let message = {}; + + // |message.headers| + let headers = message["headers"] = {}; + let receivers = aParams.receivers; + if (receivers.length != 0) { + let headersTo = headers["to"] = []; + for (let i = 0; i < receivers.length; i++) { + headersTo.push({"address": receivers[i], "type": "PLMN"}); + } + } + if (aParams.subject) { + headers["subject"] = aParams.subject; + } + + // |message.parts| + let attachments = aParams.attachments; + if (attachments.length != 0 || aParams.smil) { + let parts = message["parts"] = []; + + // Set the SMIL part if needed. + if (aParams.smil) { + let part = { + "headers": { + "content-type": { + "media": "application/smil", + }, + }, + "content": aParams.smil + }; + parts.push(part); + } + + // Set other parts for attachments if needed. + for (let i = 0; i < attachments.length; i++) { + let attachment = attachments[i]; + let content = attachment.content; + let part = { + "headers": { + "content-type": { + "media": content.type + }, + "content-length": content.size, + "content-location": attachment.location, + "content-id": attachment.id + }, + "content": content + }; + parts.push(part); + } + } + + // The following attributes are needed for saving message into DB. + message["type"] = "mms"; + message["deliveryStatusRequested"] = true; + message["timestamp"] = Date.now(); + message["receivers"] = receivers; + + debug("createSavableFromParams: message: " + JSON.stringify(message)); + return message; + }, + + createMmsMessageFromRecord: function createMmsMessageFromRecord(aRecord) { + debug("createMmsMessageFromRecord: aRecord: " + JSON.stringify(aRecord)); + + let headers = aRecord["headers"]; + let subject = headers["subject"]; + if (subject == undefined) { + subject = ""; + } + let smil = ""; + let attachments = []; + let parts = aRecord.parts; + if (parts) { + for (let i = 0; i < parts.length; i++) { + let part = parts[i]; + let partHeaders = part["headers"]; + let partContent = part["content"]; + // Don't need to make the SMIL part if it's present. + if (partHeaders["content-type"]["media"] == "application/smil") { + smil = part.content; + continue; + } + attachments.push({ + "id": partHeaders["content-id"], + "location": partHeaders["content-location"], + "content": partContent + }); + } + } + + debug("createMmsMessageFromRecord: attachments: " + JSON.stringify(attachments)); + return gMobileMessageService.createMmsMessage(aRecord.id, + aRecord.delivery, + aRecord.deliveryStatus, + aRecord.sender, + aRecord.receivers, + aRecord.timestamp, + aRecord.read, + subject, + smil, + attachments); + }, + // nsIMmsService - hasSupport: function hasSupport() { - return true; + send: function send(aParams, aRequest) { + debug("send: aParams: " + JSON.stringify(aParams)); + if (aParams.receivers.length == 0) { + aRequest.notifySendMmsMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); + return; + } + + let self = this; + + let sendTransactionCb = function sendTransactionCb(aIsSentSuccess, aMmsMessage) { + debug("sendTransactionCb: aIsSentSuccess: " + aIsSentSuccess); + gMobileMessageDatabaseService + .setMessageDelivery(aMmsMessage.id, + null, + aIsSentSuccess ? "sent" : "error", + aIsSentSuccess ? null : "error", + function notifySetDeliveryResult(setDeliveryRv, record) { + debug("Marking the delivery state/staus is done. Notify sent or failed."); + if (!aIsSentSuccess) { + aRequest.notifySendMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); + return; + } + aRequest.notifyMessageSent(aMmsMessage); + }); + }; + + let savableMessage = this.createSavableFromParams(aParams); + gMobileMessageDatabaseService + .saveSendingMessage(savableMessage, + function notifySendingResult(sendingRv, sendingRecord) { + debug("Saving sending message is done. Start to send."); + let mmsMessage = self.createMmsMessageFromRecord(sendingRecord); + let sendTransaction; + try { + sendTransaction = new SendTransaction(sendingRecord); + } catch (e) { + debug("Fail to create a SendTransaction instance."); + sendTransactionCb(false, mmsMessage); + return; + } + sendTransaction.run(function callback(aMmsStatus, aMsg) { + let isSentSuccess = (aMmsStatus == MMS.MMS_PDU_ERROR_OK); + debug("The returned status of sendTransaction.run(): " + aMmsStatus); + sendTransactionCb(isSentSuccess, mmsMessage); + }); + }); }, // nsIWapPushApplication diff --git a/dom/mobilemessage/interfaces/moz.build b/dom/mobilemessage/interfaces/moz.build index c35809783ef..86eb9374f41 100644 --- a/dom/mobilemessage/interfaces/moz.build +++ b/dom/mobilemessage/interfaces/moz.build @@ -17,6 +17,7 @@ XPIDL_SOURCES += [ 'nsIDOMSmsSegmentInfo.idl', 'nsIMobileMessageCallback.idl', 'nsIMobileMessageDatabaseService.idl', + 'nsIMobileMessageService.idl', 'nsISmsService.idl', ] diff --git a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl index 1cf2be8f7f1..bb3c3c042df 100644 --- a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl +++ b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl @@ -9,6 +9,14 @@ interface nsIDOMMozSmsRequest; interface nsIDOMMozSmsFilter; interface nsIDOMMozSmsSegmentInfo; +dictionary MmsParameters +{ + jsval receivers; // DOMString[] + DOMString? subject; + DOMString? smil; + jsval attachments; // MmsAttachment[] +}; + [scriptable, builtinclass, uuid(228508d0-7fe4-11e2-a028-83810f98f20b)] interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget { diff --git a/dom/mobilemessage/interfaces/nsIMobileMessageService.idl b/dom/mobilemessage/interfaces/nsIMobileMessageService.idl new file mode 100644 index 00000000000..30219a0391a --- /dev/null +++ b/dom/mobilemessage/interfaces/nsIMobileMessageService.idl @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIDOMMozSmsMessage; +interface nsIDOMMozMmsMessage; +interface nsIDOMMozSmsSegmentInfo; + +%{C++ +#define MOBILE_MESSAGE_SERVICE_CID { 0x829c1dd6, 0x0466, 0x4591, { 0x83, 0x6f, 0xb8, 0xf6, 0xfd, 0x1f, 0x7b, 0xa5 } } +#define MOBILE_MESSAGE_SERVICE_CONTRACTID "@mozilla.org/mobilemessage/mobilemessageservice;1" +%} + +[scriptable, builtinclass, uuid(4cbc9594-84c3-11e2-a274-ebada93fa6cd)] +interface nsIMobileMessageService : nsISupports +{ + [implicit_jscontext] + nsIDOMMozSmsMessage createSmsMessage(in long id, + in DOMString delivery, + in DOMString deliveryStatus, + in DOMString sender, + in DOMString receiver, + in DOMString body, + in DOMString messageClass, + in jsval timestamp, + in bool read); + + [implicit_jscontext] + nsIDOMMozMmsMessage createMmsMessage(in long id, + in DOMString state, + in jsval deliveryStatus, + in DOMString sender, + in jsval receivers, + in jsval timestamp, + in boolean read, + in DOMString subject, + in DOMString smil, + in jsval attachments); + + nsIDOMMozSmsSegmentInfo createSmsSegmentInfo(in long segments, + in long charsPerSegment, + in long charsAvailableInLastSegment); +}; diff --git a/dom/mobilemessage/interfaces/nsISmsService.idl b/dom/mobilemessage/interfaces/nsISmsService.idl index 7d0c6e2ba40..d50014d999d 100644 --- a/dom/mobilemessage/interfaces/nsISmsService.idl +++ b/dom/mobilemessage/interfaces/nsISmsService.idl @@ -13,7 +13,7 @@ interface nsIMobileMessageCallback; #define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1" %} -[scriptable, builtinclass, uuid(5d066568-86d2-11e2-a0d0-7351fb5ae50a)] +[scriptable, builtinclass, uuid(c4b2ed2a-8714-11e2-bd2b-13f1a0759342)] interface nsISmsService : nsISupports { boolean hasSupport(); @@ -23,19 +23,4 @@ interface nsISmsService : nsISupports void send(in DOMString number, in DOMString message, in nsIMobileMessageCallback request); - - [implicit_jscontext] - nsIDOMMozSmsMessage createSmsMessage(in long id, - in DOMString delivery, - in DOMString deliveryStatus, - in DOMString sender, - in DOMString receiver, - in DOMString body, - in DOMString messageClass, - in jsval timestamp, - in bool read); - - nsIDOMMozSmsSegmentInfo createSmsSegmentInfo(in long segments, - in long charsPerSegment, - in long charsAvailableInLastSegment); }; diff --git a/dom/mobilemessage/src/Makefile.in b/dom/mobilemessage/src/Makefile.in index 9de9d9bacab..1b42a1aade2 100644 --- a/dom/mobilemessage/src/Makefile.in +++ b/dom/mobilemessage/src/Makefile.in @@ -47,6 +47,7 @@ EXPORTS_mozilla/dom = \ EXPORTS_mozilla/dom/mobilemessage = \ SmsChild.h \ SmsParent.h \ + MobileMessageService.h \ SmsServicesFactory.h \ Constants.h \ Types.h \ @@ -57,6 +58,7 @@ CPPSRCS = \ SmsManager.cpp \ MobileMessageManager.cpp \ SmsService.cpp \ + MobileMessageService.cpp \ SmsIPCService.cpp \ SmsServicesFactory.cpp \ SmsParent.cpp \ diff --git a/dom/mobilemessage/src/MobileMessageService.cpp b/dom/mobilemessage/src/MobileMessageService.cpp new file mode 100644 index 00000000000..ba0f578ba1c --- /dev/null +++ b/dom/mobilemessage/src/MobileMessageService.cpp @@ -0,0 +1,99 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SmsMessage.h" +#include "MmsMessage.h" +#include "MobileMessageService.h" +#include "SmsSegmentInfo.h" +#include "jsapi.h" + +namespace mozilla { +namespace dom { +namespace mobilemessage { + +NS_IMPL_ISUPPORTS1(MobileMessageService, nsIMobileMessageService) + +/* static */ StaticRefPtr MobileMessageService::sSingleton; + +/* static */ already_AddRefed +MobileMessageService::GetInstance() +{ + if (!sSingleton) { + sSingleton = new MobileMessageService(); + ClearOnShutdown(&sSingleton); + } + + nsRefPtr service = sSingleton.get(); + return service.forget(); +} + +NS_IMETHODIMP +MobileMessageService::CreateSmsMessage(int32_t aId, + const nsAString& aDelivery, + const nsAString& aDeliveryStatus, + const nsAString& aSender, + const nsAString& aReceiver, + const nsAString& aBody, + const nsAString& aMessageClass, + const jsval& aTimestamp, + const bool aRead, + JSContext* aCx, + nsIDOMMozSmsMessage** aMessage) +{ + return SmsMessage::Create(aId, + aDelivery, + aDeliveryStatus, + aSender, + aReceiver, + aBody, + aMessageClass, + aTimestamp, + aRead, + aCx, + aMessage); +} + +NS_IMETHODIMP +MobileMessageService::CreateMmsMessage(int32_t aId, + const nsAString& aState, + const JS::Value& aDeliveryStatus, + const nsAString& aSender, + const JS::Value& aReceivers, + const JS::Value& aTimestamp, + bool aRead, + const nsAString& aSubject, + const nsAString& aSmil, + const JS::Value& aAttachments, + JSContext* aCx, + nsIDOMMozMmsMessage** aMessage) +{ + return MmsMessage::Create(aId, + aState, + aDeliveryStatus, + aSender, + aReceivers, + aTimestamp, + aRead, + aSubject, + aSmil, + aAttachments, + aCx, + aMessage); +} + +NS_IMETHODIMP +MobileMessageService::CreateSmsSegmentInfo(int32_t aSegments, + int32_t aCharsPerSegment, + int32_t aCharsAvailableInLastSegment, + nsIDOMMozSmsSegmentInfo** aSegmentInfo) +{ + nsCOMPtr info = + new SmsSegmentInfo(aSegments, aCharsPerSegment, aCharsAvailableInLastSegment); + info.forget(aSegmentInfo); + return NS_OK; +} + +} // namespace mobilemessage +} // namespace dom +} // namespace mozilla diff --git a/dom/mobilemessage/src/MobileMessageService.h b/dom/mobilemessage/src/MobileMessageService.h new file mode 100644 index 00000000000..936b51c6956 --- /dev/null +++ b/dom/mobilemessage/src/MobileMessageService.h @@ -0,0 +1,33 @@ +/* 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 mozilla_dom_mobilemessage_MobileMessageService_h +#define mozilla_dom_mobilemessage_MobileMessageService_h + +#include "nsIMobileMessageService.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPtr.h" + +namespace mozilla { +namespace dom { +namespace mobilemessage { + +class MobileMessageService MOZ_FINAL : public nsIMobileMessageService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMOBILEMESSAGESERVICE + + static already_AddRefed GetInstance(); + +private: + static StaticRefPtr sSingleton; + +}; + +} // namespace mobilemessage +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_mobilemessage_MobileMessageService_h diff --git a/dom/mobilemessage/src/android/SmsService.cpp b/dom/mobilemessage/src/android/SmsService.cpp index 15da95f0bdb..7affaa025b5 100644 --- a/dom/mobilemessage/src/android/SmsService.cpp +++ b/dom/mobilemessage/src/android/SmsService.cpp @@ -51,37 +51,6 @@ SmsService::Send(const nsAString& aNumber, const nsAString& aMessage, return NS_OK; } -NS_IMETHODIMP -SmsService::CreateSmsMessage(int32_t aId, - const nsAString& aDelivery, - const nsAString& aDeliveryStatus, - const nsAString& aSender, - const nsAString& aReceiver, - const nsAString& aBody, - const nsAString& aMessageClass, - const jsval& aTimestamp, - const bool aRead, - JSContext* aCx, - nsIDOMMozSmsMessage** aMessage) -{ - return SmsMessage::Create(aId, aDelivery, aDeliveryStatus, - aSender, aReceiver, - aBody, aMessageClass, aTimestamp, aRead, - aCx, aMessage); -} - -NS_IMETHODIMP -SmsService::CreateSmsSegmentInfo(int32_t aSegments, - int32_t aCharsPerSegment, - int32_t aCharsAvailableInLastSegment, - nsIDOMMozSmsSegmentInfo** aSegmentInfo) -{ - nsCOMPtr info = - new SmsSegmentInfo(aSegments, aCharsPerSegment, aCharsAvailableInLastSegment); - info.forget(aSegmentInfo); - return NS_OK; -} - } // namespace mobilemessage } // namespace dom } // namespace mozilla diff --git a/dom/mobilemessage/src/fallback/SmsService.cpp b/dom/mobilemessage/src/fallback/SmsService.cpp index 2b9c09d31f7..37a68bb8871 100644 --- a/dom/mobilemessage/src/fallback/SmsService.cpp +++ b/dom/mobilemessage/src/fallback/SmsService.cpp @@ -38,37 +38,6 @@ SmsService::Send(const nsAString& aNumber, return NS_OK; } -NS_IMETHODIMP -SmsService::CreateSmsMessage(int32_t aId, - const nsAString& aDelivery, - const nsAString& aDeliveryStatus, - const nsAString& aSender, - const nsAString& aReceiver, - const nsAString& aBody, - const nsAString& aMessageClass, - const jsval& aTimestamp, - const bool aRead, - JSContext* aCx, - nsIDOMMozSmsMessage** aMessage) -{ - return SmsMessage::Create(aId, aDelivery, aDeliveryStatus, - aSender, aReceiver, - aBody, aMessageClass, aTimestamp, aRead, - aCx, aMessage); -} - -NS_IMETHODIMP -SmsService::CreateSmsSegmentInfo(int32_t aSegments, - int32_t aCharsPerSegment, - int32_t aCharsAvailableInLastSegment, - nsIDOMMozSmsSegmentInfo** aSegmentInfo) -{ - nsCOMPtr info = - new SmsSegmentInfo(aSegments, aCharsPerSegment, aCharsAvailableInLastSegment); - info.forget(aSegmentInfo); - return NS_OK; -} - } // namespace mobilemessage } // namespace dom } // namespace mozilla diff --git a/dom/mobilemessage/src/ipc/SmsIPCService.cpp b/dom/mobilemessage/src/ipc/SmsIPCService.cpp index 1a232af3523..12e79a0bdf4 100644 --- a/dom/mobilemessage/src/ipc/SmsIPCService.cpp +++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp @@ -79,37 +79,6 @@ SmsIPCService::Send(const nsAString& aNumber, return NS_OK; } -NS_IMETHODIMP -SmsIPCService::CreateSmsMessage(int32_t aId, - const nsAString& aDelivery, - const nsAString& aDeliveryStatus, - const nsAString& aSender, - const nsAString& aReceiver, - const nsAString& aBody, - const nsAString& aMessageClass, - const jsval& aTimestamp, - const bool aRead, - JSContext* aCx, - nsIDOMMozSmsMessage** aMessage) -{ - return SmsMessage::Create(aId, aDelivery, aDeliveryStatus, - aSender, aReceiver, - aBody, aMessageClass, aTimestamp, aRead, - aCx, aMessage); -} - -NS_IMETHODIMP -SmsIPCService::CreateSmsSegmentInfo(int32_t aSegments, - int32_t aCharsPerSegment, - int32_t aCharsAvailableInLastSegment, - nsIDOMMozSmsSegmentInfo** aSegmentInfo) -{ - nsCOMPtr info = - new SmsSegmentInfo(aSegments, aCharsPerSegment, aCharsAvailableInLastSegment); - info.forget(aSegmentInfo); - return NS_OK; -} - /* * Implementation of nsIMobileMessageDatabaseService. */ diff --git a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js index f7d8d11ec95..7b97b511c51 100644 --- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js +++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js @@ -46,9 +46,9 @@ const READ_WRITE = "readwrite"; const PREV = "prev"; const NEXT = "next"; -XPCOMUtils.defineLazyServiceGetter(this, "gSmsService", - "@mozilla.org/sms/smsservice;1", - "nsISmsService"); +XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService", + "@mozilla.org/mobilemessage/mobilemessageservice;1", + "nsIMobileMessageService"); XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager", "@mozilla.org/dom/indexeddb/manager;1", @@ -562,15 +562,15 @@ MobileMessageDatabaseService.prototype = { if (DEBUG) { debug("createSmsMessageFromRecord: " + JSON.stringify(aMessageRecord)); } - return gSmsService.createSmsMessage(aMessageRecord.id, - aMessageRecord.delivery, - aMessageRecord.deliveryStatus, - aMessageRecord.sender, - aMessageRecord.receiver, - aMessageRecord.body, - aMessageRecord.messageClass, - aMessageRecord.timestamp, - aMessageRecord.read); + return gMobileMessageService.createSmsMessage(aMessageRecord.id, + aMessageRecord.delivery, + aMessageRecord.deliveryStatus, + aMessageRecord.sender, + aMessageRecord.receiver, + aMessageRecord.body, + aMessageRecord.messageClass, + aMessageRecord.timestamp, + aMessageRecord.read); }, /** diff --git a/dom/mobilemessage/src/ril/SmsService.cpp b/dom/mobilemessage/src/ril/SmsService.cpp index c84c6ac177f..c363fbcf990 100644 --- a/dom/mobilemessage/src/ril/SmsService.cpp +++ b/dom/mobilemessage/src/ril/SmsService.cpp @@ -48,37 +48,6 @@ SmsService::Send(const nsAString& aNumber, return NS_OK; } -NS_IMETHODIMP -SmsService::CreateSmsMessage(int32_t aId, - const nsAString& aDelivery, - const nsAString& aDeliveryStatus, - const nsAString& aSender, - const nsAString& aReceiver, - const nsAString& aBody, - const nsAString& aMessageClass, - const jsval& aTimestamp, - const bool aRead, - JSContext* aCx, - nsIDOMMozSmsMessage** aMessage) -{ - return SmsMessage::Create(aId, aDelivery, aDeliveryStatus, - aSender, aReceiver, - aBody, aMessageClass, aTimestamp, aRead, - aCx, aMessage); -} - -NS_IMETHODIMP -SmsService::CreateSmsSegmentInfo(int32_t aSegments, - int32_t aCharsPerSegment, - int32_t aCharsAvailableInLastSegment, - nsIDOMMozSmsSegmentInfo** aSegmentInfo) -{ - nsCOMPtr info = - new SmsSegmentInfo(aSegments, aCharsPerSegment, aCharsAvailableInLastSegment); - info.forget(aSegmentInfo); - return NS_OK; -} - } // namespace mobilemessage } // namespace dom } // namespace mozilla diff --git a/dom/mobilemessage/tests/test_smsservice_createsmsmessage.js b/dom/mobilemessage/tests/test_smsservice_createsmsmessage.js index 2a58d9b2a6d..25911f47887 100644 --- a/dom/mobilemessage/tests/test_smsservice_createsmsmessage.js +++ b/dom/mobilemessage/tests/test_smsservice_createsmsmessage.js @@ -18,10 +18,10 @@ function do_check_throws(f, result, stack) { do_throw("expected result " + result + ", none thrown", stack); } -let gSmsService = Cc["@mozilla.org/sms/smsservice;1"] - .getService(Ci.nsISmsService); +let gMobileMessageService = Cc["@mozilla.org/mobilemessage/mobilemessageservice;1"] + .getService(Ci.nsIMobileMessageService); function newMessage() { - return gSmsService.createSmsMessage.apply(gSmsService, arguments); + return gMobileMessageService.createSmsMessage.apply(gMobileMessageService, arguments); } function run_test() { diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index d0dd528fbc0..e104b4f7984 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -118,9 +118,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService", "@mozilla.org/power/powermanagerservice;1", "nsIPowerManagerService"); -XPCOMUtils.defineLazyServiceGetter(this, "gSmsService", - "@mozilla.org/sms/smsservice;1", - "nsISmsService"); +XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService", + "@mozilla.org/mobilemessage/mobilemessageservice;1", + "nsIMobileMessageService"); XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService", "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1", @@ -1486,15 +1486,15 @@ RadioInterfaceLayer.prototype = { }, createSmsMessageFromRecord: function createSmsMessageFromRecord(aRecord) { - return gSmsService.createSmsMessage(aRecord.id, - aRecord.delivery, - aRecord.deliveryStatus, - aRecord.sender, - aRecord.receiver, - aRecord.body, - aRecord.messageClass, - aRecord.timestamp, - aRecord.read); + return gMobileMessageService.createSmsMessage(aRecord.id, + aRecord.delivery, + aRecord.deliveryStatus, + aRecord.sender, + aRecord.receiver, + aRecord.body, + aRecord.messageClass, + aRecord.timestamp, + aRecord.read); }, portAddressedSmsApps: null, @@ -2657,9 +2657,9 @@ RadioInterfaceLayer.prototype = { charsInLastSegment /= 2; } - let result = gSmsService.createSmsSegmentInfo(options.segmentMaxSeq, - options.segmentChars, - options.segmentChars - charsInLastSegment); + let result = gMobileMessageService.createSmsSegmentInfo(options.segmentMaxSeq, + options.segmentChars, + options.segmentChars - charsInLastSegment); return result; }, diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index 2d38d84d1cf..bd771a344b9 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -21,7 +21,8 @@ dictionaries = [ [ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ], [ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ], [ 'SmsThreadListItem', 'nsIMobileMessageCallback.idl' ], - [ 'MmsAttachment', 'nsIDOMMozMmsMessage.idl' ] + [ 'MmsAttachment', 'nsIDOMMozMmsMessage.idl' ], + [ 'MmsParameters', 'nsIDOMMobileMessageManager.idl' ] ] # include file names diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 9453b2ef1fb..99e550153ea 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -219,7 +219,9 @@ static void Shutdown(); #include "nsDeviceSensors.h" #include "nsCSPService.h" #include "nsISmsService.h" +#include "nsIMobileMessageService.h" #include "nsIMobileMessageDatabaseService.h" +#include "mozilla/dom/mobilemessage/MobileMessageService.h" #include "mozilla/dom/mobilemessage/SmsServicesFactory.h" #include "nsIPowerManagerService.h" #include "nsIAlarmHalService.h" @@ -302,8 +304,12 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHapticFeedback) #endif #endif NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ThirdPartyUtil, Init) -NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISmsService, SmsServicesFactory::CreateSmsService) -NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileMessageDatabaseService, SmsServicesFactory::CreateMobileMessageDatabaseService) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISmsService, + SmsServicesFactory::CreateSmsService) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileMessageService, + MobileMessageService::GetInstance) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileMessageDatabaseService, + SmsServicesFactory::CreateMobileMessageDatabaseService) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPowerManagerService, PowerManagerService::GetInstance) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIAlarmHalService, @@ -822,6 +828,7 @@ NS_DEFINE_NAMED_CID(NS_HAPTICFEEDBACK_CID); #endif #endif NS_DEFINE_NAMED_CID(SMS_SERVICE_CID); +NS_DEFINE_NAMED_CID(MOBILE_MESSAGE_SERVICE_CID); NS_DEFINE_NAMED_CID(MOBILE_MESSAGE_DATABASE_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_POWERMANAGERSERVICE_CID); NS_DEFINE_NAMED_CID(OSFILECONSTANTSSERVICE_CID); @@ -1100,6 +1107,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kTHIRDPARTYUTIL_CID, false, NULL, ThirdPartyUtilConstructor }, { &kNS_STRUCTUREDCLONECONTAINER_CID, false, NULL, nsStructuredCloneContainerConstructor }, { &kSMS_SERVICE_CID, false, NULL, nsISmsServiceConstructor }, + { &kMOBILE_MESSAGE_SERVICE_CID, false, NULL, nsIMobileMessageServiceConstructor }, { &kMOBILE_MESSAGE_DATABASE_SERVICE_CID, false, NULL, nsIMobileMessageDatabaseServiceConstructor }, { &kNS_POWERMANAGERSERVICE_CID, false, NULL, nsIPowerManagerServiceConstructor }, { &kOSFILECONSTANTSSERVICE_CID, true, NULL, OSFileConstantsServiceConstructor }, @@ -1242,6 +1250,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { THIRDPARTYUTIL_CONTRACTID, &kTHIRDPARTYUTIL_CID }, { NS_STRUCTUREDCLONECONTAINER_CONTRACTID, &kNS_STRUCTUREDCLONECONTAINER_CID }, { SMS_SERVICE_CONTRACTID, &kSMS_SERVICE_CID }, + { MOBILE_MESSAGE_SERVICE_CONTRACTID, &kMOBILE_MESSAGE_SERVICE_CID }, { MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID, &kMOBILE_MESSAGE_DATABASE_SERVICE_CID }, { POWERMANAGERSERVICE_CONTRACTID, &kNS_POWERMANAGERSERVICE_CID }, { OSFILECONSTANTSSERVICE_CONTRACTID, &kOSFILECONSTANTSSERVICE_CID }, From 489f230373a8188603232359a8661ecb02f6b964 Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:22:32 +0800 Subject: [PATCH 043/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 5, nsIDOMMobileMessageManager.send()). r=vicamo,mounir sr=sicking a=leo+ --- dom/mms/interfaces/nsIMmsService.idl | 4 ++++ .../interfaces/nsIDOMMobileMessageManager.idl | 6 +++++- .../src/MobileMessageManager.cpp | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/dom/mms/interfaces/nsIMmsService.idl b/dom/mms/interfaces/nsIMmsService.idl index b8cb8c463f2..f542896eafd 100644 --- a/dom/mms/interfaces/nsIMmsService.idl +++ b/dom/mms/interfaces/nsIMmsService.idl @@ -6,6 +6,10 @@ interface nsIMobileMessageCallback; interface nsIDOMBlob; +%{C++ +#define RIL_MMSSERVICE_CONTRACTID "@mozilla.org/mms/rilmmsservice;1" +%} + [scriptable, uuid(3ec33286-8559-11e2-9f38-e76b58650568)] interface nsIMmsService : nsISupports { diff --git a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl index bb3c3c042df..1bcbf1e5bfd 100644 --- a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl +++ b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl @@ -8,6 +8,8 @@ interface nsIDOMEventListener; interface nsIDOMMozSmsRequest; interface nsIDOMMozSmsFilter; interface nsIDOMMozSmsSegmentInfo; +interface nsIDOMDOMRequest; +interface nsIDOMBlob; dictionary MmsParameters { @@ -17,7 +19,7 @@ dictionary MmsParameters jsval attachments; // MmsAttachment[] }; -[scriptable, builtinclass, uuid(228508d0-7fe4-11e2-a028-83810f98f20b)] +[scriptable, builtinclass, uuid(f020e48e-84c7-11e2-939e-53a3106dde16)] interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget { nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text); @@ -28,6 +30,8 @@ interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget // An array of SmsRequest objects otherwise. jsval send(in jsval number, in DOMString message); + nsIDOMDOMRequest sendMMS(in jsval parameters /* MmsParameters */); + [binaryname(GetMessageMoz)] nsIDOMMozSmsRequest getMessage(in long id); diff --git a/dom/mobilemessage/src/MobileMessageManager.cpp b/dom/mobilemessage/src/MobileMessageManager.cpp index 2410b9b8a9b..e22f91e29e3 100644 --- a/dom/mobilemessage/src/MobileMessageManager.cpp +++ b/dom/mobilemessage/src/MobileMessageManager.cpp @@ -7,6 +7,7 @@ #include "MobileMessageManager.h" #include "nsIDOMClassInfo.h" #include "nsISmsService.h" +#include "nsIMmsService.h" #include "nsIObserverService.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" @@ -20,6 +21,9 @@ #include "nsIXPConnect.h" #include "nsIPermissionManager.h" #include "GeneratedEvents.h" +#include "DOMRequest.h" +#include "nsIMobileMessageCallback.h" +#include "MobileMessageCallback.h" #define RECEIVED_EVENT_NAME NS_LITERAL_STRING("received") #define SENDING_EVENT_NAME NS_LITERAL_STRING("sending") @@ -173,6 +177,21 @@ MobileMessageManager::Send(const jsval& aNumber, const nsAString& aMessage, jsva return NS_OK; } +NS_IMETHODIMP +MobileMessageManager::SendMMS(const JS::Value& aParams, nsIDOMDOMRequest** aRequest) +{ + nsCOMPtr mmsService = do_GetService(RIL_MMSSERVICE_CONTRACTID); + NS_ENSURE_TRUE(mmsService, NS_ERROR_FAILURE); + + nsRefPtr request = new DOMRequest(GetOwner()); + nsCOMPtr msgCallback = new MobileMessageCallback(request); + nsresult rv = mmsService->Send(aParams, msgCallback); + NS_ENSURE_SUCCESS(rv, rv); + + request.forget(aRequest); + return NS_OK; +} + NS_IMETHODIMP MobileMessageManager::GetMessageMoz(int32_t aId, nsIDOMMozSmsRequest** aRequest) { From 1aab17284ebf20cd0f67b8e806ae3ba875036a5e Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Sat, 9 Mar 2013 15:22:42 +0800 Subject: [PATCH 044/202] Bug 844431 - B2G MMS: provide nsIDOMMobileMessageManager interface (with sendMMS() first) (part 6, dispatch MMS events). r=vicamo,mounir sr=sicking a=leo+ --- content/events/src/nsEventDispatcher.cpp | 2 + dom/mms/src/ril/MmsService.js | 15 ++++- dom/mobilemessage/interfaces/moz.build | 1 + .../interfaces/nsIDOMMozMmsEvent.idl | 23 ++++++++ dom/mobilemessage/src/Constants.cpp | 4 ++ dom/mobilemessage/src/Constants.h | 4 ++ .../src/MobileMessageManager.cpp | 59 ++++++++++++++++++- dom/mobilemessage/src/MobileMessageManager.h | 4 ++ .../mochitest/general/test_interfaces.html | 1 + js/xpconnect/src/event_impl_gen.conf.in | 1 + 10 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 dom/mobilemessage/interfaces/nsIDOMMozMmsEvent.idl diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index fb2d63b0815..fb1cb05edb8 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -860,6 +860,8 @@ nsEventDispatcher::CreateEvent(mozilla::dom::EventTarget* aOwner, return NS_NewDOMCustomEvent(aDOMEvent, aOwner, aPresContext, nullptr); if (aEventType.LowerCaseEqualsLiteral("mozsmsevent")) return NS_NewDOMMozSmsEvent(aDOMEvent, aOwner, aPresContext, nullptr); + if (aEventType.LowerCaseEqualsLiteral("mozmmsevent")) + return NS_NewDOMMozMmsEvent(aDOMEvent, aOwner, aPresContext, nullptr); if (aEventType.LowerCaseEqualsLiteral("storageevent")) { return NS_NewDOMStorageEvent(aDOMEvent, aOwner, aPresContext, nullptr); } diff --git a/dom/mms/src/ril/MmsService.js b/dom/mms/src/ril/MmsService.js index 0e52dfb857b..e1670de6dad 100644 --- a/dom/mms/src/ril/MmsService.js +++ b/dom/mms/src/ril/MmsService.js @@ -18,6 +18,10 @@ const RIL_MMSSERVICE_CID = Components.ID("{217ddd76-75db-4210-955d-8806cd8d87f9} const DEBUG = false; +const kMmsSendingObserverTopic = "mms-sending"; +const kMmsSentObserverTopic = "mms-sent"; +const kMmsFailedObserverTopic = "mms-failed"; + const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed"; const kXpcomShutdownObserverTopic = "xpcom-shutdown"; const kPrefenceChangedObserverTopic = "nsPref:changed"; @@ -1098,7 +1102,13 @@ MmsService.prototype = { * The MMS message object. */ handleDeliveryIndication: function handleDeliveryIndication(msg) { - // TODO: bug 811252 - implement MMS database + // TODO Bug 850140 Two things we need to do in the future: + // + // 1. Use gMobileMessageDatabaseService.setMessageDelivery() to reset + // the delivery status to "success" or "error" for a specific receiver. + // + // 2. Fire "mms-delivery-success" or "mms-delivery-error" observer + // topics to MobileMessageManager. let messageId = msg.headers["message-id"]; debug("handleDeliveryIndication: got delivery report for " + messageId); }, @@ -1231,9 +1241,11 @@ MmsService.prototype = { debug("Marking the delivery state/staus is done. Notify sent or failed."); if (!aIsSentSuccess) { aRequest.notifySendMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); + Services.obs.notifyObservers(aMmsMessage, kMmsFailedObserverTopic, null); return; } aRequest.notifyMessageSent(aMmsMessage); + Services.obs.notifyObservers(aMmsMessage, kMmsSentObserverTopic, null); }); }; @@ -1243,6 +1255,7 @@ MmsService.prototype = { function notifySendingResult(sendingRv, sendingRecord) { debug("Saving sending message is done. Start to send."); let mmsMessage = self.createMmsMessageFromRecord(sendingRecord); + Services.obs.notifyObservers(mmsMessage, kMmsSendingObserverTopic, null); let sendTransaction; try { sendTransaction = new SendTransaction(sendingRecord); diff --git a/dom/mobilemessage/interfaces/moz.build b/dom/mobilemessage/interfaces/moz.build index 86eb9374f41..813ce0b635a 100644 --- a/dom/mobilemessage/interfaces/moz.build +++ b/dom/mobilemessage/interfaces/moz.build @@ -5,6 +5,7 @@ XPIDL_SOURCES += [ 'nsIDOMMobileMessageManager.idl', + 'nsIDOMMozMmsEvent.idl', 'nsIDOMMozMmsMessage.idl', 'nsIDOMMozSmsEvent.idl', 'nsIDOMMozSmsMessage.idl', diff --git a/dom/mobilemessage/interfaces/nsIDOMMozMmsEvent.idl b/dom/mobilemessage/interfaces/nsIDOMMozMmsEvent.idl new file mode 100644 index 00000000000..019b895bb97 --- /dev/null +++ b/dom/mobilemessage/interfaces/nsIDOMMozMmsEvent.idl @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIDOMEvent.idl" + +interface nsIDOMMozMmsMessage; + +[scriptable, builtinclass, uuid(b33cc0f2-8886-11e2-9433-eff9a8af9a70)] +interface nsIDOMMozMmsEvent : nsIDOMEvent +{ + readonly attribute nsIDOMMozMmsMessage message; + + [noscript] void initMozMmsEvent(in DOMString aType, + in boolean aCanBubble, + in boolean aCancelable, + in nsIDOMMozMmsMessage aMessage); +}; + +dictionary MozMmsEventInit : EventInit +{ + nsIDOMMozMmsMessage message; +}; diff --git a/dom/mobilemessage/src/Constants.cpp b/dom/mobilemessage/src/Constants.cpp index ee80b760b86..5cb6c886265 100644 --- a/dom/mobilemessage/src/Constants.cpp +++ b/dom/mobilemessage/src/Constants.cpp @@ -14,6 +14,10 @@ const char* kSmsFailedObserverTopic = "sms-failed"; const char* kSmsDeliverySuccessObserverTopic = "sms-delivery-success"; const char* kSmsDeliveryErrorObserverTopic = "sms-delivery-error"; +const char* kMmsSendingObserverTopic = "mms-sending"; +const char* kMmsSentObserverTopic = "mms-sent"; +const char* kMmsFailedObserverTopic = "mms-failed"; + } // namespace mobilemessage } // namespace dom } // namespace mozilla diff --git a/dom/mobilemessage/src/Constants.h b/dom/mobilemessage/src/Constants.h index 2acbff11fd7..50ce55f864c 100644 --- a/dom/mobilemessage/src/Constants.h +++ b/dom/mobilemessage/src/Constants.h @@ -18,6 +18,10 @@ extern const char* kSmsFailedObserverTopic; extern const char* kSmsDeliverySuccessObserverTopic; extern const char* kSmsDeliveryErrorObserverTopic; +extern const char* kMmsSendingObserverTopic; +extern const char* kMmsSentObserverTopic; +extern const char* kMmsFailedObserverTopic; + #define DELIVERY_RECEIVED NS_LITERAL_STRING("received") #define DELIVERY_SENDING NS_LITERAL_STRING("sending") #define DELIVERY_SENT NS_LITERAL_STRING("sent") diff --git a/dom/mobilemessage/src/MobileMessageManager.cpp b/dom/mobilemessage/src/MobileMessageManager.cpp index e22f91e29e3..e754eaa2385 100644 --- a/dom/mobilemessage/src/MobileMessageManager.cpp +++ b/dom/mobilemessage/src/MobileMessageManager.cpp @@ -13,7 +13,9 @@ #include "mozilla/Services.h" #include "Constants.h" #include "nsIDOMMozSmsEvent.h" +#include "nsIDOMMozMmsEvent.h" #include "nsIDOMMozSmsMessage.h" +#include "nsIDOMMozMmsMessage.h" #include "SmsRequest.h" #include "nsJSUtils.h" #include "nsContentUtils.h" @@ -72,6 +74,10 @@ MobileMessageManager::Init(nsPIDOMWindow *aWindow) obs->AddObserver(this, kSmsFailedObserverTopic, false); obs->AddObserver(this, kSmsDeliverySuccessObserverTopic, false); obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false); + + obs->AddObserver(this, kMmsSendingObserverTopic, false); + obs->AddObserver(this, kMmsSentObserverTopic, false); + obs->AddObserver(this, kMmsFailedObserverTopic, false); } void @@ -89,6 +95,10 @@ MobileMessageManager::Shutdown() obs->RemoveObserver(this, kSmsFailedObserverTopic); obs->RemoveObserver(this, kSmsDeliverySuccessObserverTopic); obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic); + + obs->RemoveObserver(this, kMmsSendingObserverTopic); + obs->RemoveObserver(this, kMmsSentObserverTopic); + obs->RemoveObserver(this, kMmsFailedObserverTopic); } NS_IMETHODIMP @@ -305,13 +315,27 @@ MobileMessageManager::DispatchTrustedSmsEventToSelf(const nsAString& aEventName, NS_ASSERTION(event, "This should never fail!"); nsCOMPtr se = do_QueryInterface(event); - MOZ_ASSERT(se); nsresult rv = se->InitMozSmsEvent(aEventName, false, false, aMessage); NS_ENSURE_SUCCESS(rv, rv); return DispatchTrustedEvent(event); } +nsresult +MobileMessageManager::DispatchTrustedMmsEventToSelf(const nsAString& aEventName, + nsIDOMMozMmsMessage* aMessage) +{ + nsCOMPtr event; + NS_NewDOMMozMmsEvent(getter_AddRefs(event), this, nullptr, nullptr); + NS_ASSERTION(event, "This should never fail!"); + + nsCOMPtr se = do_QueryInterface(event); + nsresult rv = se->InitMozMmsEvent(aEventName, false, false, aMessage); + NS_ENSURE_SUCCESS(rv, rv); + + return DispatchTrustedEvent(event); +} + NS_IMETHODIMP MobileMessageManager::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) @@ -382,6 +406,39 @@ MobileMessageManager::Observe(nsISupports* aSubject, const char* aTopic, return NS_OK; } + if (!strcmp(aTopic, kMmsSendingObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'mms-sending' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedMmsEventToSelf(SENDING_EVENT_NAME, message); + return NS_OK; + } + + if (!strcmp(aTopic, kMmsSentObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'mms-sent' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedMmsEventToSelf(SENT_EVENT_NAME, message); + return NS_OK; + } + + if (!strcmp(aTopic, kMmsFailedObserverTopic)) { + nsCOMPtr message = do_QueryInterface(aSubject); + if (!message) { + NS_ERROR("Got a 'mms-failed' topic without a valid message!"); + return NS_OK; + } + + DispatchTrustedMmsEventToSelf(FAILED_EVENT_NAME, message); + return NS_OK; + } + return NS_OK; } diff --git a/dom/mobilemessage/src/MobileMessageManager.h b/dom/mobilemessage/src/MobileMessageManager.h index 1a04f4fb447..4d0b0ae91c2 100644 --- a/dom/mobilemessage/src/MobileMessageManager.h +++ b/dom/mobilemessage/src/MobileMessageManager.h @@ -11,6 +11,7 @@ #include "nsDOMEventTargetHelper.h" class nsIDOMMozSmsMessage; +class nsIDOMMozMmsMessage; namespace mozilla { namespace dom { @@ -43,6 +44,9 @@ private: nsresult DispatchTrustedSmsEventToSelf(const nsAString& aEventName, nsIDOMMozSmsMessage* aMessage); + + nsresult DispatchTrustedMmsEventToSelf(const nsAString& aEventName, + nsIDOMMozMmsMessage* aMessage); }; } // namespace dom diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 2bd14e09ea4..f0401dd2d5e 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -263,6 +263,7 @@ var interfaceNamesInGlobalScope = "SVGPathSegLinetoRel", "HTMLImageElement", "MozSmsEvent", + "MozMmsEvent", "CustomEvent", "XMLHttpRequestUpload", "SVGFEFuncBElement", diff --git a/js/xpconnect/src/event_impl_gen.conf.in b/js/xpconnect/src/event_impl_gen.conf.in index 8edc838cc4d..0b94d0e37aa 100644 --- a/js/xpconnect/src/event_impl_gen.conf.in +++ b/js/xpconnect/src/event_impl_gen.conf.in @@ -41,6 +41,7 @@ simple_events = [ #endif 'ElementReplaceEvent', 'MozSmsEvent', + 'MozMmsEvent', 'DeviceStorageChangeEvent', 'PopupBlockedEvent', 'BlobEvent' From 6b8bd5e0d5b0a4e1625c05f63c032823feb9d225 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Fri, 15 Mar 2013 11:50:17 +0900 Subject: [PATCH 045/202] Bug 837598 - cannot build WebRTC for Linux/arm. r=glandium --- build/autoconf/arch.m4 | 5 +++++ configure.in | 21 ++++++++++++++------- js/src/build/autoconf/arch.m4 | 5 +++++ media/webrtc/shared_libs.mk | 4 ++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/build/autoconf/arch.m4 b/build/autoconf/arch.m4 index 0310234d8ba..e318158a057 100644 --- a/build/autoconf/arch.m4 +++ b/build/autoconf/arch.m4 @@ -213,6 +213,11 @@ if test "$CPU_ARCH" = "arm"; then AC_DEFINE(HAVE_ARM_NEON) HAVE_ARM_NEON=1 fi + + AC_MSG_CHECKING(ARM version support in compiler) + dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.) + ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]]*\).*/\1/p'` + AC_MSG_RESULT("$ARM_ARCH") fi # CPU_ARCH = arm AC_SUBST(HAVE_ARM_SIMD) diff --git a/configure.in b/configure.in index 21c264afee1..7597dac3e47 100644 --- a/configure.in +++ b/configure.in @@ -3677,8 +3677,6 @@ fi dnl Using the custom linker on ARMv6 requires 16k alignment of ELF segments. if test -n "$MOZ_LINKER"; then if test "$CPU_ARCH" = arm; then - dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.) - ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]]*\).*/\1/p'` dnl When building for < ARMv7, we need to ensure 16k alignment of ELF segments if test -n "$ARM_ARCH" && test "$ARM_ARCH" -lt 7; then LDFLAGS="$LDFLAGS -Wl,-z,max-page-size=0x4000 -Wl,-z,common-page-size=0x4000" @@ -9040,11 +9038,20 @@ if test "${OS_TARGET}" = "WINNT"; then elif test "${OS_TARGET}" = "Android"; then EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android " - if test -n "$ARM_ARCH" && test "$ARM_ARCH" -lt 7; then - EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 " - else - EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=1 " - fi +fi + +if test -n "$ARM_ARCH"; then + if test "$ARM_ARCH" -lt 7; then + EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 " + else + if test "${OS_TARGET}" = "Android"; then + EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=1 " + else + dnl CPU detection for ARM works on Android only. armv7 always uses CPU detection, so + dnl we have to set armv7=0 for non-Android target + EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 " + fi + fi fi # Don't try to compile sse4.1 code if toolchain doesn't support diff --git a/js/src/build/autoconf/arch.m4 b/js/src/build/autoconf/arch.m4 index 0310234d8ba..e318158a057 100644 --- a/js/src/build/autoconf/arch.m4 +++ b/js/src/build/autoconf/arch.m4 @@ -213,6 +213,11 @@ if test "$CPU_ARCH" = "arm"; then AC_DEFINE(HAVE_ARM_NEON) HAVE_ARM_NEON=1 fi + + AC_MSG_CHECKING(ARM version support in compiler) + dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.) + ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]]*\).*/\1/p'` + AC_MSG_RESULT("$ARM_ARCH") fi # CPU_ARCH = arm AC_SUBST(HAVE_ARM_SIMD) diff --git a/media/webrtc/shared_libs.mk b/media/webrtc/shared_libs.mk index e1281292d41..33380bb4075 100644 --- a/media/webrtc/shared_libs.mk +++ b/media/webrtc/shared_libs.mk @@ -47,6 +47,9 @@ WEBRTC_LIBS += \ endif ifeq ($(CPU_ARCH), arm) +ifeq (Android,$(OS_TARGET)) +# NEON detection on WebRTC is Android only. If WebRTC supports Linux/arm etc, +# we should remove OS check # extra ARM libs WEBRTC_LIBS += \ $(call EXPAND_LIBNAME_PATH,cpu_features_android,$(DEPTH)/media/webrtc/trunk/webrtc/system_wrappers/source/system_wrappers_cpu_features_android) \ @@ -59,6 +62,7 @@ WEBRTC_LIBS += \ $(NULL) endif endif +endif # If you enable one of these codecs in webrtc_config.gypi, you'll need to re-add the From 418152901b0cefc4528dcefed1a7052e7262f0ef Mon Sep 17 00:00:00 2001 From: Gina Yeh Date: Fri, 15 Mar 2013 15:15:47 +0800 Subject: [PATCH 046/202] Bug 834590 - Cache adapter path in BluetoothService, r=echou --- dom/bluetooth/BluetoothAdapter.cpp | 24 +- dom/bluetooth/BluetoothCommon.h | 6 +- dom/bluetooth/BluetoothDevice.cpp | 4 +- dom/bluetooth/BluetoothManager.cpp | 4 +- dom/bluetooth/BluetoothPropertyContainer.cpp | 6 +- dom/bluetooth/BluetoothService.cpp | 9 +- dom/bluetooth/BluetoothService.h | 21 +- dom/bluetooth/ipc/BluetoothParent.cpp | 11 +- .../ipc/BluetoothServiceChildProcess.cpp | 19 +- .../ipc/BluetoothServiceChildProcess.h | 16 +- dom/bluetooth/ipc/PBluetooth.ipdl | 6 - dom/bluetooth/linux/BluetoothDBusService.cpp | 404 ++++++++++-------- dom/bluetooth/linux/BluetoothDBusService.h | 33 +- 13 files changed, 275 insertions(+), 288 deletions(-) diff --git a/dom/bluetooth/BluetoothAdapter.cpp b/dom/bluetooth/BluetoothAdapter.cpp index 0af2873f7df..4180a0cde3b 100644 --- a/dom/bluetooth/BluetoothAdapter.cpp +++ b/dom/bluetooth/BluetoothAdapter.cpp @@ -164,7 +164,7 @@ BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow, BluetoothService* bs = BluetoothService::Get(); NS_ENSURE_TRUE_VOID(bs); - bs->RegisterBluetoothSignalHandler(mPath, this); + bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this); } BluetoothAdapter::~BluetoothAdapter() @@ -172,7 +172,7 @@ BluetoothAdapter::~BluetoothAdapter() BluetoothService* bs = BluetoothService::Get(); // We can be null on shutdown, where this might happen NS_ENSURE_TRUE_VOID(bs); - bs->UnregisterBluetoothSignalHandler(mPath, this); + bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this); Unroot(); } @@ -301,9 +301,8 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData) NS_ASSERTION(aData.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue, "DeviceCreated: Invalid value type"); - nsRefPtr device = BluetoothDevice::Create(GetOwner(), - GetPath(), - aData.value()); + nsRefPtr device = + BluetoothDevice::Create(GetOwner(), GetPath(), aData.value()); nsCOMPtr event; NS_NewDOMBluetoothDeviceEvent(getter_AddRefs(event), this, nullptr, nullptr); @@ -344,9 +343,9 @@ BluetoothAdapter::StartStopDiscovery(bool aStart, nsIDOMDOMRequest** aRequest) BluetoothService* bs = BluetoothService::Get(); NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); if (aStart) { - rv = bs->StartDiscoveryInternal(mPath, results); + rv = bs->StartDiscoveryInternal(results); } else { - rv = bs->StopDiscoveryInternal(mPath, results); + rv = bs->StopDiscoveryInternal(results); } if(NS_FAILED(rv)) { NS_WARNING("Start/Stop Discovery failed!"); @@ -459,7 +458,7 @@ BluetoothAdapter::SetName(const nsAString& aName, BluetoothNamedValue property(NS_LITERAL_STRING("Name"), value); return SetProperty(GetOwner(), property, aRequest); } - + NS_IMETHODIMP BluetoothAdapter::SetDiscoverable(const bool aDiscoverable, nsIDOMDOMRequest** aRequest) @@ -520,18 +519,17 @@ BluetoothAdapter::PairUnpair(bool aPair, nsRefPtr results = new BluetoothVoidReplyRunnable(req); - nsString addr; + nsAutoString addr; aDevice->GetAddress(addr); BluetoothService* bs = BluetoothService::Get(); NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); if (aPair) { - rv = bs->CreatePairedDeviceInternal(mPath, - addr, + rv = bs->CreatePairedDeviceInternal(addr, kCreatePairedDeviceTimeout, results); } else { - rv = bs->RemoveDeviceInternal(mPath, addr, results); + rv = bs->RemoveDeviceInternal(addr, results); } if (NS_FAILED(rv)) { @@ -668,7 +666,7 @@ BluetoothAdapter::Connect(const nsAString& aDeviceAddress, nsRefPtr results = new BluetoothVoidReplyRunnable(req); - bs->Connect(aDeviceAddress, mPath, aProfileId, results); + bs->Connect(aDeviceAddress, aProfileId, results); req.forget(aRequest); return NS_OK; diff --git a/dom/bluetooth/BluetoothCommon.h b/dom/bluetooth/BluetoothCommon.h index 65facac45d9..d5a52f9fba7 100644 --- a/dom/bluetooth/BluetoothCommon.h +++ b/dom/bluetooth/BluetoothCommon.h @@ -46,8 +46,10 @@ extern bool gBluetoothDebugFlag; #define USING_BLUETOOTH_NAMESPACE \ using namespace mozilla::dom::bluetooth; -#define LOCAL_AGENT_PATH "/B2G/bluetooth/agent" -#define REMOTE_AGENT_PATH "/B2G/bluetooth/remote_device_agent" +#define KEY_LOCAL_AGENT "/B2G/bluetooth/agent" +#define KEY_REMOTE_AGENT "/B2G/bluetooth/remote_device_agent" +#define KEY_MANAGER "/B2G/bluetooth/manager" +#define KEY_ADAPTER "/B2G/bluetooth/adapter" // Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx) #define BLUETOOTH_ADDRESS_LENGTH 17 diff --git a/dom/bluetooth/BluetoothDevice.cpp b/dom/bluetooth/BluetoothDevice.cpp index 8280cda3a7f..a966f79c190 100644 --- a/dom/bluetooth/BluetoothDevice.cpp +++ b/dom/bluetooth/BluetoothDevice.cpp @@ -64,7 +64,7 @@ BluetoothDevice::BluetoothDevice(nsPIDOMWindow* aWindow, BluetoothService* bs = BluetoothService::Get(); NS_ENSURE_TRUE_VOID(bs); - bs->RegisterBluetoothSignalHandler(mPath, this); + bs->RegisterBluetoothSignalHandler(mAddress, this); } BluetoothDevice::~BluetoothDevice() @@ -72,7 +72,7 @@ BluetoothDevice::~BluetoothDevice() BluetoothService* bs = BluetoothService::Get(); // bs can be null on shutdown, where destruction might happen. NS_ENSURE_TRUE_VOID(bs); - bs->UnregisterBluetoothSignalHandler(mPath, this); + bs->UnregisterBluetoothSignalHandler(mAddress, this); Unroot(); } diff --git a/dom/bluetooth/BluetoothManager.cpp b/dom/bluetooth/BluetoothManager.cpp index adb2c59da92..b08d5ff932a 100644 --- a/dom/bluetooth/BluetoothManager.cpp +++ b/dom/bluetooth/BluetoothManager.cpp @@ -102,14 +102,14 @@ BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow) BluetoothService* bs = BluetoothService::Get(); NS_ENSURE_TRUE_VOID(bs); - bs->RegisterBluetoothSignalHandler(mPath, this); + bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this); } BluetoothManager::~BluetoothManager() { BluetoothService* bs = BluetoothService::Get(); NS_ENSURE_TRUE_VOID(bs); - bs->UnregisterBluetoothSignalHandler(mPath, this); + bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this); } void diff --git a/dom/bluetooth/BluetoothPropertyContainer.cpp b/dom/bluetooth/BluetoothPropertyContainer.cpp index 03586d76f13..803f7f5edea 100644 --- a/dom/bluetooth/BluetoothPropertyContainer.cpp +++ b/dom/bluetooth/BluetoothPropertyContainer.cpp @@ -55,10 +55,10 @@ BluetoothPropertyContainer::SetProperty(nsIDOMWindow* aOwner, } nsRefPtr task = new BluetoothVoidReplyRunnable(req); - - rv = bs->SetProperty(mObjectType, mPath, aProperty, task); + + rv = bs->SetProperty(mObjectType, aProperty, task); NS_ENSURE_SUCCESS(rv, rv); - + req.forget(aRequest); return NS_OK; } diff --git a/dom/bluetooth/BluetoothService.cpp b/dom/bluetooth/BluetoothService.cpp index 86e40ca1314..cbc0f41674d 100644 --- a/dom/bluetooth/BluetoothService.cpp +++ b/dom/bluetooth/BluetoothService.cpp @@ -8,6 +8,7 @@ #include "BluetoothService.h" +#include "BluetoothCommon.h" #include "BluetoothManager.h" #include "BluetoothParent.h" #include "BluetoothReplyRunnable.h" @@ -136,7 +137,7 @@ public: } else { signalName = NS_LITERAL_STRING("Disabled"); } - signalPath = NS_LITERAL_STRING("/"); + signalPath = NS_LITERAL_STRING(KEY_MANAGER); BluetoothSignal signal(signalName, signalPath, v); gBluetoothService->DistributeSignal(signal); } @@ -276,7 +277,7 @@ RemoveObserversExceptBluetoothManager nsAutoPtr& value, void* arg) { - if (!key.EqualsLiteral("/")) { + if (!key.EqualsLiteral(KEY_MANAGER)) { return PL_DHASH_REMOVE; } @@ -409,10 +410,10 @@ BluetoothService::DistributeSignal(const BluetoothSignal& aSignal) { MOZ_ASSERT(NS_IsMainThread()); - if (aSignal.path().EqualsLiteral(LOCAL_AGENT_PATH)) { + if (aSignal.path().EqualsLiteral(KEY_LOCAL_AGENT)) { Notify(aSignal); return; - } else if (aSignal.path().EqualsLiteral(REMOTE_AGENT_PATH)) { + } else if (aSignal.path().EqualsLiteral(KEY_REMOTE_AGENT)) { Notify(aSignal); return; } diff --git a/dom/bluetooth/BluetoothService.h b/dom/bluetooth/BluetoothService.h index 1a83080467d..6d583d56828 100644 --- a/dom/bluetooth/BluetoothService.h +++ b/dom/bluetooth/BluetoothService.h @@ -134,24 +134,18 @@ public: /** * Stop device discovery (platform specific implementation) * - * @param aAdapterPath Adapter to stop discovery on - * * @return NS_OK if discovery stopped correctly, false otherwise */ virtual nsresult - StopDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable) = 0; + StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) = 0; /** * Start device discovery (platform specific implementation) * - * @param aAdapterPath Adapter to start discovery on - * * @return NS_OK if discovery stopped correctly, false otherwise */ virtual nsresult - StartDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable) = 0; + StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) = 0; /** * Fetches the propertes for the specified device @@ -166,7 +160,6 @@ public: /** * Set a property for the specified object * - * @param aPath Path to the object * @param aPropName Name of the property * @param aValue Boolean value * @param aRunnable Runnable to run on async reply @@ -175,7 +168,6 @@ public: */ virtual nsresult SetProperty(BluetoothObjectType aType, - const nsAString& aPath, const BluetoothNamedValue& aValue, BluetoothReplyRunnable* aRunnable) = 0; @@ -194,14 +186,12 @@ public: nsAString& aDevicePath) = 0; virtual nsresult - CreatePairedDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aAddress, + CreatePairedDeviceInternal(const nsAString& aAddress, int aTimeout, BluetoothReplyRunnable* aRunnable) = 0; virtual nsresult - RemoveDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aObjectPath, + RemoveDeviceInternal(const nsAString& aObjectPath, BluetoothReplyRunnable* aRunnable) = 0; virtual nsresult @@ -236,11 +226,10 @@ public: BluetoothReplyRunnable* aRunnable) = 0; virtual nsresult - PrepareAdapterInternal(const nsAString& aPath) = 0; + PrepareAdapterInternal() = 0; virtual void Connect(const nsAString& aDeviceAddress, - const nsAString& aAdapterPath, uint16_t aProfileId, BluetoothReplyRunnable* aRunnable) = 0; diff --git a/dom/bluetooth/ipc/BluetoothParent.cpp b/dom/bluetooth/ipc/BluetoothParent.cpp index 0d89f17ebdb..af5c8e3ed39 100644 --- a/dom/bluetooth/ipc/BluetoothParent.cpp +++ b/dom/bluetooth/ipc/BluetoothParent.cpp @@ -309,7 +309,7 @@ BluetoothRequestParent::DoRequest(const SetPropertyRequest& aRequest) MOZ_ASSERT(mRequestType == Request::TSetPropertyRequest); nsresult rv = - mService->SetProperty(aRequest.type(), aRequest.path(), aRequest.value(), + mService->SetProperty(aRequest.type(), aRequest.value(), mReplyRunnable.get()); NS_ENSURE_SUCCESS(rv, false); @@ -323,7 +323,7 @@ BluetoothRequestParent::DoRequest(const StartDiscoveryRequest& aRequest) MOZ_ASSERT(mRequestType == Request::TStartDiscoveryRequest); nsresult rv = - mService->StartDiscoveryInternal(aRequest.path(), mReplyRunnable.get()); + mService->StartDiscoveryInternal(mReplyRunnable.get()); NS_ENSURE_SUCCESS(rv, false); return true; @@ -336,7 +336,7 @@ BluetoothRequestParent::DoRequest(const StopDiscoveryRequest& aRequest) MOZ_ASSERT(mRequestType == Request::TStopDiscoveryRequest); nsresult rv = - mService->StopDiscoveryInternal(aRequest.path(), mReplyRunnable.get()); + mService->StopDiscoveryInternal(mReplyRunnable.get()); NS_ENSURE_SUCCESS(rv, false); return true; @@ -349,7 +349,7 @@ BluetoothRequestParent::DoRequest(const PairRequest& aRequest) MOZ_ASSERT(mRequestType == Request::TPairRequest); nsresult rv = - mService->CreatePairedDeviceInternal(aRequest.path(), aRequest.address(), + mService->CreatePairedDeviceInternal(aRequest.address(), aRequest.timeoutMS(), mReplyRunnable.get()); NS_ENSURE_SUCCESS(rv, false); @@ -364,7 +364,7 @@ BluetoothRequestParent::DoRequest(const UnpairRequest& aRequest) MOZ_ASSERT(mRequestType == Request::TUnpairRequest); nsresult rv = - mService->RemoveDeviceInternal(aRequest.path(), aRequest.address(), + mService->RemoveDeviceInternal(aRequest.address(), mReplyRunnable.get()); NS_ENSURE_SUCCESS(rv, false); @@ -490,7 +490,6 @@ BluetoothRequestParent::DoRequest(const ConnectRequest& aRequest) MOZ_ASSERT(mRequestType == Request::TConnectRequest); mService->Connect(aRequest.address(), - aRequest.adapterPath(), aRequest.profileId(), mReplyRunnable.get()); diff --git a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp index a6259a52bcb..3e4f5c4e0b3 100644 --- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp +++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp @@ -124,29 +124,26 @@ BluetoothServiceChildProcess::GetPairedDevicePropertiesInternal( nsresult BluetoothServiceChildProcess::StopDiscoveryInternal( - const nsAString& aAdapterPath, BluetoothReplyRunnable* aRunnable) { - SendRequest(aRunnable, StopDiscoveryRequest(nsString(aAdapterPath))); + SendRequest(aRunnable, StopDiscoveryRequest()); return NS_OK; } nsresult BluetoothServiceChildProcess::StartDiscoveryInternal( - const nsAString& aAdapterPath, BluetoothReplyRunnable* aRunnable) { - SendRequest(aRunnable, StartDiscoveryRequest(nsString(aAdapterPath))); + SendRequest(aRunnable, StartDiscoveryRequest()); return NS_OK; } nsresult BluetoothServiceChildProcess::SetProperty(BluetoothObjectType aType, - const nsAString& aPath, const BluetoothNamedValue& aValue, BluetoothReplyRunnable* aRunnable) { - SendRequest(aRunnable, SetPropertyRequest(aType, nsString(aPath), aValue)); + SendRequest(aRunnable, SetPropertyRequest(aType, aValue)); return NS_OK; } @@ -171,24 +168,22 @@ BluetoothServiceChildProcess::GetDevicePath(const nsAString& aAdapterPath, nsresult BluetoothServiceChildProcess::CreatePairedDeviceInternal( - const nsAString& aAdapterPath, const nsAString& aAddress, int aTimeout, BluetoothReplyRunnable* aRunnable) { SendRequest(aRunnable, - PairRequest(nsString(aAdapterPath), nsString(aAddress), aTimeout)); + PairRequest(nsString(aAddress), aTimeout)); return NS_OK; } nsresult BluetoothServiceChildProcess::RemoveDeviceInternal( - const nsAString& aAdapterPath, const nsAString& aObjectPath, BluetoothReplyRunnable* aRunnable) { SendRequest(aRunnable, - UnpairRequest(nsString(aAdapterPath), nsString(aObjectPath))); + UnpairRequest(nsString(aObjectPath))); return NS_OK; } @@ -285,7 +280,7 @@ BluetoothServiceChildProcess::SetAuthorizationInternal( } nsresult -BluetoothServiceChildProcess::PrepareAdapterInternal(const nsAString& aPath) +BluetoothServiceChildProcess::PrepareAdapterInternal() { MOZ_NOT_REACHED("Should never be called from child"); return NS_ERROR_NOT_IMPLEMENTED; @@ -294,13 +289,11 @@ BluetoothServiceChildProcess::PrepareAdapterInternal(const nsAString& aPath) void BluetoothServiceChildProcess::Connect( const nsAString& aDeviceAddress, - const nsAString& aAdapterPath, const uint16_t aProfileId, BluetoothReplyRunnable* aRunnable) { SendRequest(aRunnable, ConnectRequest(nsString(aDeviceAddress), - nsString(aAdapterPath), aProfileId)); } diff --git a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h index a5ded3ef7e8..3e4610a039b 100644 --- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h +++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h @@ -52,16 +52,13 @@ public: MOZ_OVERRIDE; virtual nsresult - StopDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; + StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; virtual nsresult - StartDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; + StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; virtual nsresult SetProperty(BluetoothObjectType aType, - const nsAString& aPath, const BluetoothNamedValue& aValue, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; @@ -71,14 +68,12 @@ public: nsAString& aDevicePath) MOZ_OVERRIDE; virtual nsresult - CreatePairedDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aAddress, + CreatePairedDeviceInternal(const nsAString& aAddress, int aTimeout, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; virtual nsresult - RemoveDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aObjectPath, + RemoveDeviceInternal(const nsAString& aObjectPath, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; virtual nsresult @@ -126,7 +121,6 @@ public: virtual void Connect(const nsAString& aDeviceAddress, - const nsAString& aAdapterPath, const uint16_t aProfileId, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE; @@ -182,7 +176,7 @@ private: // This method should never be called from the child. virtual nsresult - PrepareAdapterInternal(const nsAString& aPath) MOZ_OVERRIDE; + PrepareAdapterInternal() MOZ_OVERRIDE; }; END_BLUETOOTH_NAMESPACE diff --git a/dom/bluetooth/ipc/PBluetooth.ipdl b/dom/bluetooth/ipc/PBluetooth.ipdl index 863dc441297..b5876de0d67 100644 --- a/dom/bluetooth/ipc/PBluetooth.ipdl +++ b/dom/bluetooth/ipc/PBluetooth.ipdl @@ -28,7 +28,6 @@ struct DefaultAdapterPathRequest struct SetPropertyRequest { BluetoothObjectType type; - nsString path; BluetoothNamedValue value; }; @@ -40,24 +39,20 @@ struct GetPropertyRequest struct StartDiscoveryRequest { - nsString path; }; struct StopDiscoveryRequest { - nsString path; }; struct PairRequest { - nsString path; nsString address; uint32_t timeoutMS; }; struct UnpairRequest { - nsString path; nsString address; }; @@ -101,7 +96,6 @@ struct DevicePropertiesRequest struct ConnectRequest { nsString address; - nsString adapterPath; uint16_t profileId; }; diff --git a/dom/bluetooth/linux/BluetoothDBusService.cpp b/dom/bluetooth/linux/BluetoothDBusService.cpp index 2707efe1006..84a7c8b38b3 100644 --- a/dom/bluetooth/linux/BluetoothDBusService.cpp +++ b/dom/bluetooth/linux/BluetoothDBusService.cpp @@ -150,14 +150,11 @@ typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString& class RemoveDeviceTask : public nsRunnable { public: - RemoveDeviceTask(const nsAString& aAdapterPath, - const nsACString& aDeviceObjectPath, + RemoveDeviceTask(const nsACString& aDeviceObjectPath, BluetoothReplyRunnable* aRunnable) - : mAdapterPath(aAdapterPath) - , mDeviceObjectPath(aDeviceObjectPath) + : mDeviceObjectPath(aDeviceObjectPath) , mRunnable(aRunnable) { - MOZ_ASSERT(!aAdapterPath.IsEmpty()); MOZ_ASSERT(!aDeviceObjectPath.IsEmpty()); MOZ_ASSERT(aRunnable); } @@ -167,13 +164,13 @@ public: MOZ_ASSERT(!NS_IsMainThread()); BluetoothValue v = true; - nsString errorStr; + nsAutoString errorStr; const char* tempDeviceObjectPath = mDeviceObjectPath.get(); DBusMessage *reply = dbus_func_args(gThreadConnection->GetConnection(), - NS_ConvertUTF16toUTF8(mAdapterPath).get(), + NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "RemoveDevice", DBUS_TYPE_OBJECT_PATH, &tempDeviceObjectPath, DBUS_TYPE_INVALID); @@ -190,18 +187,15 @@ public: } private: - nsString mAdapterPath; nsCString mDeviceObjectPath; nsRefPtr mRunnable; }; class SendDiscoveryTask : public nsRunnable { public: - SendDiscoveryTask(const nsAString& aAdapterPath, - const char* aMessageName, + SendDiscoveryTask(const char* aMessageName, BluetoothReplyRunnable* aRunnable) - : mAdapterPath(aAdapterPath) - , mMessageName(aMessageName) + : mMessageName(aMessageName) , mRunnable(aRunnable) { MOZ_ASSERT(aMessageName); @@ -214,7 +208,7 @@ public: DBusMessage *reply = dbus_func_args(gThreadConnection->GetConnection(), - NS_ConvertUTF16toUTF8(mAdapterPath).get(), + NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, mMessageName, DBUS_TYPE_INVALID); @@ -223,14 +217,13 @@ public: } BluetoothValue v = true; - nsString errorStr; + nsAutoString errorStr; DispatchBluetoothReply(mRunnable, v, errorStr); return NS_OK; } private: - nsString mAdapterPath; const char* mMessageName; nsRefPtr mRunnable; }; @@ -270,16 +263,14 @@ public: MOZ_ASSERT(NS_IsMainThread()); BluetoothService* bs = BluetoothService::Get(); - if (!bs) { - NS_WARNING("BluetoothService not available!"); - return NS_ERROR_FAILURE; - } + NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); + sAdapterPath = mPath; // Due to the fact that we need to queue the dbus call to the command thread // inside the bluetoothservice, we have to route the call down to the main // thread and then back out to the command thread. There has to be a better // way to do this. - if (NS_FAILED(bs->PrepareAdapterInternal(mPath))) { + if (NS_FAILED(bs->PrepareAdapterInternal())) { NS_WARNING("Prepare adapter failed"); return NS_ERROR_FAILURE; } @@ -437,8 +428,8 @@ AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) // http://maemo.org/api_refs/5.0/beta/bluez/agent.html // if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Cancel")) { - // This method gets called to indicate that the agent request failed before a reply - // was returned. + // This method gets called to indicate that the agent request failed before + // a reply was returned. // Return directly DBusMessage *reply = dbus_message_new_method_return(msg); @@ -473,7 +464,8 @@ AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) NS_ConvertUTF8toUTF16(uuid))); // Because we may have authorization request and pairing request from the - // same remote device at the same time, we need two tables to keep these messages. + // same remote device at the same time, we need two tables to keep these + // messages. sAuthorizeReqTable.Put(deviceAddress, msg); // Increase ref count here because we need this message later. @@ -482,9 +474,10 @@ AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) v = parameters; } - } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestConfirmation")) { - // This method gets called when the service daemon needs to confirm a passkey for - // an authentication. + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, + "RequestConfirmation")) { + // This method gets called when the service daemon needs to confirm a + // passkey for an authentication. char *objectPath; uint32_t passkey; if (!dbus_message_get_args(msg, NULL, @@ -516,10 +509,11 @@ AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) // Since we're handling this in other threads, just fall out here return DBUS_HANDLER_RESULT_HANDLED; } - } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPinCode")) { - // This method gets called when the service daemon needs to get the passkey for an - // authentication. The return value should be a string of 1-16 characters length. - // The string can be alphanumeric. + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, + "RequestPinCode")) { + // This method gets called when the service daemon needs to get the passkey + // for an authentication. The return value should be a string of 1-16 + // characters length. The string can be alphanumeric. char *objectPath; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &objectPath, @@ -546,9 +540,11 @@ AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) // Since we're handling this in other threads, just fall out here return DBUS_HANDLER_RESULT_HANDLED; } - } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPasskey")) { - // This method gets called when the service daemon needs to get the passkey for an - // authentication. The return value should be a numeric value between 0-999999. + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, + "RequestPasskey")) { + // This method gets called when the service daemon needs to get the passkey + // for an authentication. The return value should be a numeric value + // between 0-999999. char *objectPath; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &objectPath, @@ -576,9 +572,10 @@ AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) return DBUS_HANDLER_RESULT_HANDLED; } } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) { - // This method gets called when the service daemon unregisters the agent. An agent - // can use it to do cleanup tasks. There is no need to unregister the agent, because - // when this method gets called it has already been unregistered. + // This method gets called when the service daemon unregisters the agent. + // An agent can use it to do cleanup tasks. There is no need to unregister + // the agent, because when this method gets called it has already been + // unregistered. DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { @@ -634,7 +631,7 @@ RegisterLocalAgent(const char* adapterPath, &agentVtable, NULL)) { BT_WARNING("%s: Can't register object path %s for agent!", - __FUNCTION__, agentPath); + __FUNCTION__, agentPath); return false; } @@ -684,25 +681,25 @@ RegisterLocalAgent(const char* adapterPath, } static bool -RegisterAgent(const nsAString& aAdapterPath) +RegisterAgent() { MOZ_ASSERT(!NS_IsMainThread()); - if (!RegisterLocalAgent(NS_ConvertUTF16toUTF8(aAdapterPath).get(), - LOCAL_AGENT_PATH, + if (!RegisterLocalAgent(NS_ConvertUTF16toUTF8(sAdapterPath).get(), + KEY_LOCAL_AGENT, B2G_AGENT_CAPABILITIES)) { return false; } // There is no "RegisterAgent" function defined in device interface. - // When we call "CreatePairedDevice", it will do device agent registration for us. - // (See maemo.org/api_refs/5.0/beta/bluez/adapter.html) + // When we call "CreatePairedDevice", it will do device agent registration + // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html) if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(), - REMOTE_AGENT_PATH, + KEY_REMOTE_AGENT, &agentVtable, NULL)) { BT_WARNING("%s: Can't register object path %s for remote device agent!", - __FUNCTION__, REMOTE_AGENT_PATH); + __FUNCTION__, KEY_REMOTE_AGENT); return false; } @@ -736,8 +733,7 @@ ExtractHandles(DBusMessage *aReply, nsTArray& aOutHandles) // static bool -BluetoothDBusService::AddServiceRecords(const nsAString& aAdapterPath, - const char* serviceName, +BluetoothDBusService::AddServiceRecords(const char* serviceName, unsigned long long uuidMsb, unsigned long long uuidLsb, int channel) @@ -746,7 +742,7 @@ BluetoothDBusService::AddServiceRecords(const nsAString& aAdapterPath, DBusMessage *reply; reply = dbus_func_args(gThreadConnection->GetConnection(), - NS_ConvertUTF16toUTF8(aAdapterPath).get(), + NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord", DBUS_TYPE_STRING, &serviceName, DBUS_TYPE_UINT64, &uuidMsb, @@ -759,9 +755,9 @@ BluetoothDBusService::AddServiceRecords(const nsAString& aAdapterPath, // static bool -BluetoothDBusService::AddReservedServicesInternal(const nsAString& aAdapterPath, - const nsTArray& aServices, - nsTArray& aServiceHandlesContainer) +BluetoothDBusService::AddReservedServicesInternal( + const nsTArray& aServices, + nsTArray& aServiceHandlesContainer) { MOZ_ASSERT(!NS_IsMainThread()); @@ -771,7 +767,7 @@ BluetoothDBusService::AddReservedServicesInternal(const nsAString& aAdapterPath, const uint32_t* services = aServices.Elements(); DBusMessage* reply = dbus_func_args(gThreadConnection->GetConnection(), - NS_ConvertUTF16toUTF8(aAdapterPath).get(), + NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "AddReservedServiceRecords", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &services, length, DBUS_TYPE_INVALID); @@ -857,8 +853,7 @@ public: class PrepareAdapterRunnable : public nsRunnable { public: - PrepareAdapterRunnable(const nsAString& aPath) : - mPath(aPath) + PrepareAdapterRunnable() { MOZ_ASSERT(NS_IsMainThread()); } @@ -878,14 +873,14 @@ public: // so we could clean it up. For right now though, we can throw it away. nsTArray handles; - if (!BluetoothDBusService::AddReservedServicesInternal(mPath, uuids, handles)) { + if (!BluetoothDBusService::AddReservedServicesInternal(uuids, handles)) { NS_WARNING("Failed to add reserved services"); #ifdef MOZ_WIDGET_GONK return NS_ERROR_FAILURE; #endif } - if(!RegisterAgent(mPath)) { + if(!RegisterAgent()) { NS_WARNING("Failed to register agent"); return NS_ERROR_FAILURE; } @@ -893,9 +888,6 @@ public: NS_DispatchToMainThread(new PrepareProfileManagersRunnable()); return NS_OK; } - -private: - nsString mPath; }; void @@ -914,7 +906,7 @@ RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable, NS_ASSERTION(replyRunnable, "Callback reply runnable is null!"); - nsString replyError; + nsAutoString replyError; BluetoothValue v; aFunc(aMsg, nullptr, v, replyError); DispatchBluetoothReply(replyRunnable, v, replyError); @@ -1054,7 +1046,7 @@ GetProperty(DBusMessageIter aIter, Properties* aPropertyTypes, return false; } - nsString propertyName; + nsAutoString propertyName; propertyName.AssignASCII(aPropertyTypes[i].name); *aPropIndex = i; @@ -1102,7 +1094,7 @@ GetProperty(DBusMessageIter aIter, Properties* aPropertyTypes, do { const char* tmp; dbus_message_iter_get_basic(&array_val_iter, &tmp); - nsString s; + nsAutoString s; s = NS_ConvertUTF8toUTF16(tmp); arr.AppendElement(s); } while (dbus_message_iter_next(&array_val_iter)); @@ -1119,9 +1111,7 @@ GetProperty(DBusMessageIter aIter, Properties* aPropertyTypes, // This happens when the array is 0-length. Apparently we get a // DBUS_TYPE_INVALID type. propertyValue = InfallibleTArray(); -#ifdef DEBUG NS_WARNING("Received array type that's not a string array!"); -#endif } break; default: @@ -1191,15 +1181,19 @@ UnpackAdapterPropertiesMessage(DBusMessage* aMsg, DBusError* aErr, } bool -ReplaceConnectedType(Properties* sourceProperties, Properties** destProperties, int aPropertyTypeLen) +ReplaceConnectedType(Properties* sourceProperties, + Properties** destProperties, + int aPropertyTypeLen) { if (!IsDeviceConnectedTypeBoolean()) { return false; } - *destProperties = (Properties *) malloc(sizeof(Properties) * aPropertyTypeLen); + *destProperties = (Properties*)malloc(sizeof(Properties) * aPropertyTypeLen); if (*destProperties) { CopyProperties(sourceProperties, *destProperties, aPropertyTypeLen); - int index = GetPropertyIndex(*destProperties, "Connected", aPropertyTypeLen); + int index = GetPropertyIndex(*destProperties, + "Connected", + aPropertyTypeLen); if (index >= 0) { (*destProperties)[index].type = DBUS_TYPE_BOOLEAN; return true; @@ -1217,7 +1211,8 @@ UnpackDevicePropertiesMessage(DBusMessage* aMsg, DBusError* aErr, { Properties* props = sDeviceProperties; Properties* newProps; - bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, ArrayLength(sDeviceProperties)); + bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, + ArrayLength(sDeviceProperties)); if (replaced) { props = newProps; } @@ -1267,7 +1262,8 @@ static DBusCallback sBluetoothDBusPropCallbacks[] = GetDevicePropertiesCallback }; -MOZ_STATIC_ASSERT(sizeof(sBluetoothDBusPropCallbacks) == sizeof(sBluetoothDBusIfaces), +MOZ_STATIC_ASSERT( + sizeof(sBluetoothDBusPropCallbacks) == sizeof(sBluetoothDBusIfaces), "DBus Property callback array and DBus interface array must be same size"); void @@ -1296,11 +1292,13 @@ ParsePropertyChange(DBusMessage* aMsg, BluetoothValue& aValue, } bool -GetPropertiesInternal(const nsAString& aPath, const char* aIface, BluetoothValue& aValue) +GetPropertiesInternal(const nsAString& aPath, + const char* aIface, + BluetoothValue& aValue) { MOZ_ASSERT(!NS_IsMainThread()); - nsString replyError; + nsAutoString replyError; DBusError err; dbus_error_init(&err); @@ -1356,18 +1354,32 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) DBusError err; dbus_error_init(&err); - nsString signalPath; - nsString signalName; + nsAutoString signalPath; + nsAutoString signalName; + nsAutoString signalInterface; - BT_LOG("%s: %s, %s", __FUNCTION__, - dbus_message_get_path(aMsg), - dbus_message_get_member(aMsg)); + BT_LOG("%s: %s, %s, %s", __FUNCTION__, + dbus_message_get_interface(aMsg), + dbus_message_get_path(aMsg), + dbus_message_get_member(aMsg)); + signalInterface = NS_ConvertUTF8toUTF16(dbus_message_get_interface(aMsg)); signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(aMsg)); signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg)); nsString errorStr; BluetoothValue v; + // Since the signalPath extracted from dbus message is a object path, + // we'd like to re-assign them to corresponding key entry in + // BluetoothSignalObserverTable + if (signalInterface.EqualsLiteral(DBUS_MANAGER_IFACE)) { + signalPath.AssignLiteral(KEY_MANAGER); + } else if (signalInterface.EqualsLiteral(DBUS_ADAPTER_IFACE)) { + signalPath.AssignLiteral(KEY_ADAPTER); + } else if (signalInterface.EqualsLiteral(DBUS_DEVICE_IFACE)){ + signalPath = GetAddressFromObjectPath(signalPath); + } + if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceFound")) { DBusMessageIter iter; @@ -1382,7 +1394,8 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) if (dbus_message_iter_next(&iter)) { Properties* props = sDeviceProperties; Properties* newProps; - bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, ArrayLength(sDeviceProperties)); + bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, + ArrayLength(sDeviceProperties)); if (replaced) { props = newProps; } @@ -1401,7 +1414,7 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) // a dict value. After we parse out the properties, we need to go back // and add the address to the ipdl dict we've created to make sure we // have all of the information to correctly build the device. - nsString addrstr = NS_ConvertUTF8toUTF16(addr); + nsAutoString addrstr = NS_ConvertUTF8toUTF16(addr); nsString path = GetObjectPathFromAddress(signalPath, addrstr); v.get_ArrayOfBluetoothNamedValue() @@ -1437,7 +1450,8 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) } else { errorStr.AssignLiteral("DBus device found message structure not as expected!"); } - } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceDisappeared")) { + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, + "DeviceDisappeared")) { const char* str; if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_STRING, &str, @@ -1447,7 +1461,8 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) } else { v = NS_ConvertUTF8toUTF16(str); } - } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceCreated")) { + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, + "DeviceCreated")) { const char* str; if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_OBJECT_PATH, &str, @@ -1458,10 +1473,18 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) v = NS_ConvertUTF8toUTF16(str); } - // Bug 842471. No need to get device properties here and forward to - // BluetoothAdapter, and this event is going to be removed from idl. + BluetoothSignal signal(signalName, signalPath, v); + + // Fire a Device properties fetcher at the main thread + nsRefPtr b = + new DevicePropertiesSignalHandler(signal); + if (NS_FAILED(NS_DispatchToMainThread(b))) { + NS_WARNING("Failed to dispatch to main thread!"); + } + // Since we're handling this in other threads, just fall out here return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceRemoved")) { + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, + "DeviceRemoved")) { const char* str; if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_OBJECT_PATH, &str, @@ -1471,16 +1494,19 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) } else { v = NS_ConvertUTF8toUTF16(str); } - } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "PropertyChanged")) { + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, + "PropertyChanged")) { ParsePropertyChange(aMsg, v, errorStr, sAdapterProperties, ArrayLength(sAdapterProperties)); - } else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE, "PropertyChanged")) { + } else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE, + "PropertyChanged")) { Properties* props = sDeviceProperties; Properties* newProps; - bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, ArrayLength(sDeviceProperties)); + bool replaced = ReplaceConnectedType(sDeviceProperties, &newProps, + ArrayLength(sDeviceProperties)); if (replaced) { props = newProps; } @@ -1492,12 +1518,14 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) if (replaced) { free(newProps); } - if (v.get_ArrayOfBluetoothNamedValue()[0].name().EqualsLiteral("Paired")) { + + BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[0]; + if (property.name().EqualsLiteral("Paired")) { // transfer signal to BluetoothService and // broadcast system message of bluetooth-pairingstatuschanged signalName = NS_LITERAL_STRING("PairedStatusChanged"); - signalPath = NS_LITERAL_STRING(LOCAL_AGENT_PATH); - v.get_ArrayOfBluetoothNamedValue()[0].name() = NS_LITERAL_STRING("paired"); + signalPath = NS_LITERAL_STRING(KEY_LOCAL_AGENT); + property.name() = NS_LITERAL_STRING("paired"); } } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) { const char* str; @@ -1513,19 +1541,18 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) NS_WARNING("Failed to dispatch to main thread!"); } } - } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "PropertyChanged")) { + } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, + "PropertyChanged")) { ParsePropertyChange(aMsg, v, errorStr, sManagerProperties, ArrayLength(sManagerProperties)); } else { -#ifdef DEBUG nsAutoCString signalStr; signalStr += dbus_message_get_member(aMsg); signalStr += " Signal not handled!"; NS_WARNING(signalStr.get()); -#endif return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1642,13 +1669,12 @@ BluetoothDBusService::StartInternal() } BluetoothValue v; - nsString replyError; + nsAutoString replyError; if (NS_FAILED(GetDefaultAdapterPath(v, replyError))) { // Adapter path is not ready yet // Let's do PrepareAdapterTask when we receive signal 'AdapterAdded' } else { - // Adapter path has been ready, and we won't receive signal 'AdapterAdded' later - // Let's do PrepareAdapterTask now + // Adapter path has been ready. let's do PrepareAdapterTask now nsRefPtr b = new PrepareAdapterTask(v.get_nsString()); if (NS_FAILED(NS_DispatchToMainThread(b))) { NS_WARNING("Failed to dispatch to main thread!"); @@ -1696,15 +1722,15 @@ BluetoothDBusService::StopInternal() dbus_connection_remove_filter(mConnection, EventFilter, nullptr); if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(), - LOCAL_AGENT_PATH)) { + KEY_LOCAL_AGENT)) { BT_WARNING("%s: Can't unregister object path %s for agent!", - __FUNCTION__, LOCAL_AGENT_PATH); + __FUNCTION__, KEY_LOCAL_AGENT); } if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(), - REMOTE_AGENT_PATH)) { + KEY_REMOTE_AGENT)) { BT_WARNING("%s: Can't unregister object path %s for agent!", - __FUNCTION__, LOCAL_AGENT_PATH); + __FUNCTION__, KEY_REMOTE_AGENT); } mConnection = nullptr; @@ -1737,7 +1763,7 @@ public: MOZ_ASSERT(!NS_IsMainThread()); BluetoothValue v; - nsString replyError; + nsAutoString replyError; if (NS_FAILED(GetDefaultAdapterPath(v, replyError))) { DispatchBluetoothReply(mRunnable, v, replyError); @@ -1768,13 +1794,14 @@ private: }; nsresult -BluetoothDBusService::GetDefaultAdapterPathInternal(BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::GetDefaultAdapterPathInternal( + BluetoothReplyRunnable* aRunnable) { NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!"); if (!IsReady()) { BluetoothValue v; - nsString errorStr; + nsAutoString errorStr; errorStr.AssignLiteral("Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, v, errorStr); return NS_OK; @@ -1792,8 +1819,7 @@ BluetoothDBusService::GetDefaultAdapterPathInternal(BluetoothReplyRunnable* aRun } nsresult -BluetoothDBusService::SendDiscoveryMessage(const nsAString& aAdapterPath, - const char* aMessageName, +BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName, BluetoothReplyRunnable* aRunnable) { NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!"); @@ -1801,14 +1827,13 @@ BluetoothDBusService::SendDiscoveryMessage(const nsAString& aAdapterPath, if (!IsReady()) { BluetoothValue v; - nsString errorStr; + nsAutoString errorStr; errorStr.AssignLiteral("Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, v, errorStr); return NS_OK; } - nsRefPtr task(new SendDiscoveryTask(aAdapterPath, - aMessageName, + nsRefPtr task(new SendDiscoveryTask(aMessageName, aRunnable)); if (NS_FAILED(mBluetoothCommandThread->Dispatch(task, NS_DISPATCH_NORMAL))) { NS_WARNING("Cannot dispatch firmware loading task!"); @@ -1819,17 +1844,15 @@ BluetoothDBusService::SendDiscoveryMessage(const nsAString& aAdapterPath, } nsresult -BluetoothDBusService::StopDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) { - return SendDiscoveryMessage(aAdapterPath, "StopDiscovery", aRunnable); + return SendDiscoveryMessage("StopDiscovery", aRunnable); } nsresult -BluetoothDBusService::StartDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) { - return SendDiscoveryMessage(aAdapterPath, "StartDiscovery", aRunnable); + return SendDiscoveryMessage("StartDiscovery", aRunnable); } class BluetoothDevicePropertiesRunnable : public nsRunnable @@ -1845,7 +1868,6 @@ public: { MOZ_ASSERT(!NS_IsMainThread()); - nsString devicePath; BluetoothValue v = mSignal.value(); if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue || v.get_ArrayOfBluetoothNamedValue().Length() == 0) { @@ -1858,7 +1880,7 @@ public: NS_ASSERTION(arr[0].name().EqualsLiteral("path"), "failed to get object path"); NS_ASSERTION(arr[0].value().type() == BluetoothValue::TnsString, "failed to get_nsString"); - devicePath = arr[0].value().get_nsString(); + nsString devicePath = arr[0].value().get_nsString(); BluetoothValue prop; if (!GetPropertiesInternal(devicePath, DBUS_DEVICE_IFACE, prop)) { @@ -1907,8 +1929,9 @@ private: class BluetoothPairedDevicePropertiesRunnable : public nsRunnable { public: - BluetoothPairedDevicePropertiesRunnable(BluetoothReplyRunnable* aRunnable, - const nsTArray& aDeviceAddresses) + BluetoothPairedDevicePropertiesRunnable( + BluetoothReplyRunnable* aRunnable, + const nsTArray& aDeviceAddresses) : mRunnable(dont_AddRef(aRunnable)), mDeviceAddresses(aDeviceAddresses) { @@ -1989,19 +2012,21 @@ BluetoothDBusService::GetDevicePropertiesInternal(const BluetoothSignal& aSignal } nsresult -BluetoothDBusService::GetPairedDevicePropertiesInternal(const nsTArray& aDeviceAddresses, - BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::GetPairedDevicePropertiesInternal( + const nsTArray& aDeviceAddresses, + BluetoothReplyRunnable* aRunnable) { if (!IsReady()) { BluetoothValue v; - nsString errorStr; + nsAutoString errorStr; errorStr.AssignLiteral("Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, v, errorStr); return NS_OK; } nsRefPtr runnable = aRunnable; - nsRefPtr func(new BluetoothPairedDevicePropertiesRunnable(runnable, aDeviceAddresses)); + nsRefPtr func( + new BluetoothPairedDevicePropertiesRunnable(runnable, aDeviceAddresses)); if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) { NS_WARNING("Cannot dispatch task!"); return NS_ERROR_FAILURE; @@ -2013,7 +2038,6 @@ BluetoothDBusService::GetPairedDevicePropertiesInternal(const nsTArray nsresult BluetoothDBusService::SetProperty(BluetoothObjectType aType, - const nsAString& aPath, const BluetoothNamedValue& aValue, BluetoothReplyRunnable* aRunnable) { @@ -2021,7 +2045,7 @@ BluetoothDBusService::SetProperty(BluetoothObjectType aType, if (!IsReady()) { BluetoothValue v; - nsString errorStr; + nsAutoString errorStr; errorStr.AssignLiteral("Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, v, errorStr); return NS_OK; @@ -2031,10 +2055,11 @@ BluetoothDBusService::SetProperty(BluetoothObjectType aType, const char* interface = sBluetoothDBusIfaces[aType]; /* Compose the command */ - DBusMessage* msg = dbus_message_new_method_call("org.bluez", - NS_ConvertUTF16toUTF8(aPath).get(), - interface, - "SetProperty"); + DBusMessage* msg = dbus_message_new_method_call( + "org.bluez", + NS_ConvertUTF16toUTF8(sAdapterPath).get(), + interface, + "SetProperty"); if (!msg) { NS_WARNING("Could not allocate D-Bus message object!"); @@ -2043,7 +2068,8 @@ BluetoothDBusService::SetProperty(BluetoothObjectType aType, nsCString intermediatePropName(NS_ConvertUTF16toUTF8(aValue.name())); const char* propName = intermediatePropName.get(); - if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &propName, DBUS_TYPE_INVALID)) { + if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &propName, + DBUS_TYPE_INVALID)) { NS_WARNING("Couldn't append arguments to dbus message!"); return NS_ERROR_FAILURE; } @@ -2138,8 +2164,8 @@ GetDeviceServiceChannel(const nsAString& aObjectPath, // static bool -BluetoothDBusService::RemoveReservedServicesInternal(const nsAString& aAdapterPath, - const nsTArray& aServiceHandles) +BluetoothDBusService::RemoveReservedServicesInternal( + const nsTArray& aServiceHandles) { MOZ_ASSERT(!NS_IsMainThread()); @@ -2150,7 +2176,7 @@ BluetoothDBusService::RemoveReservedServicesInternal(const nsAString& aAdapterPa DBusMessage* reply = dbus_func_args(gThreadConnection->GetConnection(), - NS_ConvertUTF16toUTF8(aAdapterPath).get(), + NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "RemoveReservedServiceRecords", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &services, length, DBUS_TYPE_INVALID); @@ -2162,13 +2188,13 @@ BluetoothDBusService::RemoveReservedServicesInternal(const nsAString& aAdapterPa } nsresult -BluetoothDBusService::CreatePairedDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aDeviceAddress, - int aTimeout, - BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::CreatePairedDeviceInternal( + const nsAString& aDeviceAddress, + int aTimeout, + BluetoothReplyRunnable* aRunnable) { const char *capabilities = B2G_AGENT_CAPABILITIES; - const char *deviceAgentPath = REMOTE_AGENT_PATH; + const char *deviceAgentPath = KEY_REMOTE_AGENT; nsCString tempDeviceAddress = NS_ConvertUTF16toUTF8(aDeviceAddress); const char *deviceAddress = tempDeviceAddress.get(); @@ -2194,7 +2220,7 @@ BluetoothDBusService::CreatePairedDeviceInternal(const nsAString& aAdapterPath, aTimeout, GetObjectPathCallback, (void*)runnable, - NS_ConvertUTF16toUTF8(aAdapterPath).get(), + NS_ConvertUTF16toUTF8(sAdapterPath).get(), DBUS_ADAPTER_IFACE, "CreatePairedDevice", DBUS_TYPE_STRING, &deviceAddress, @@ -2211,24 +2237,22 @@ BluetoothDBusService::CreatePairedDeviceInternal(const nsAString& aAdapterPath, } nsresult -BluetoothDBusService::RemoveDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aDeviceAddress, +BluetoothDBusService::RemoveDeviceInternal(const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable) { if (!IsReady()) { BluetoothValue v; - nsString errorStr; + nsAutoString errorStr; errorStr.AssignLiteral("Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, v, errorStr); return NS_OK; } - nsCString tempDeviceObjectPath( - NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(aAdapterPath, - aDeviceAddress))); + nsCString tempDeviceObjectPath = + NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(sAdapterPath, + aDeviceAddress)); - nsRefPtr task(new RemoveDeviceTask(aAdapterPath, - tempDeviceObjectPath, + nsRefPtr task(new RemoveDeviceTask(tempDeviceObjectPath, aRunnable)); if (NS_FAILED(mBluetoothCommandThread->Dispatch(task, NS_DISPATCH_NORMAL))) { @@ -2244,7 +2268,7 @@ BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable) { - nsString errorStr; + nsAutoString errorStr; BluetoothValue v = true; DBusMessage *msg; if (!sPairingReqTable.Get(aDeviceAddress, &msg)) { @@ -2292,7 +2316,7 @@ BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey, BluetoothReplyRunnable* aRunnable) { - nsString errorStr; + nsAutoString errorStr; BluetoothValue v = true; DBusMessage *msg; if (!sPairingReqTable.Get(aDeviceAddress, &msg)) { @@ -2334,11 +2358,12 @@ BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, } bool -BluetoothDBusService::SetPairingConfirmationInternal(const nsAString& aDeviceAddress, - bool aConfirm, - BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::SetPairingConfirmationInternal( + const nsAString& aDeviceAddress, + bool aConfirm, + BluetoothReplyRunnable* aRunnable) { - nsString errorStr; + nsAutoString errorStr; BluetoothValue v = true; DBusMessage *msg; if (!sPairingReqTable.Get(aDeviceAddress, &msg)) { @@ -2378,11 +2403,12 @@ BluetoothDBusService::SetPairingConfirmationInternal(const nsAString& aDeviceAdd } bool -BluetoothDBusService::SetAuthorizationInternal(const nsAString& aDeviceAddress, - bool aAllow, - BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::SetAuthorizationInternal( + const nsAString& aDeviceAddress, + bool aAllow, + BluetoothReplyRunnable* aRunnable) { - nsString errorStr; + nsAutoString errorStr; BluetoothValue v = true; DBusMessage *msg; @@ -2423,7 +2449,7 @@ BluetoothDBusService::SetAuthorizationInternal(const nsAString& aDeviceAddress, } nsresult -BluetoothDBusService::PrepareAdapterInternal(const nsAString& aPath) +BluetoothDBusService::PrepareAdapterInternal() { NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!"); @@ -2432,10 +2458,7 @@ BluetoothDBusService::PrepareAdapterInternal(const nsAString& aPath) return NS_ERROR_FAILURE; } - // Keep the adapter path for further use - sAdapterPath = aPath; - - nsRefPtr func(new PrepareAdapterRunnable(aPath)); + nsRefPtr func(new PrepareAdapterRunnable()); if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) { NS_WARNING("Cannot dispatch task!"); return NS_ERROR_FAILURE; @@ -2446,33 +2469,35 @@ BluetoothDBusService::PrepareAdapterInternal(const nsAString& aPath) void BluetoothDBusService::Connect(const nsAString& aDeviceAddress, - const nsAString& aAdapterPath, const uint16_t aProfileId, BluetoothReplyRunnable* aRunnable) { NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!"); BluetoothValue v; - nsString errorStr; + nsAutoString errorStr; if (aProfileId == BluetoothServiceClass::HANDSFREE) { BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); - if (!hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress), + if (!hfp->Connect(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress), true, aRunnable)) { - errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting to a headset!"); + errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting \ + to a headset!"); DispatchBluetoothReply(aRunnable, v, errorStr); } } else if (aProfileId == BluetoothServiceClass::HEADSET) { BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); - if (!hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress), + if (!hfp->Connect(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress), false, aRunnable)) { - errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting to a headset!"); + errorStr.AssignLiteral("BluetoothHfpManager has connected/is connecting \ + to a headset!"); DispatchBluetoothReply(aRunnable, v, errorStr); } } else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) { BluetoothOppManager* opp = BluetoothOppManager::Get(); - if (!opp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress), + if (!opp->Connect(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress), aRunnable)) { - errorStr.AssignLiteral("BluetoothOppManager has connected/is connecting!"); + errorStr.AssignLiteral("BluetoothOppManager has been connected/is \ + connecting!"); DispatchBluetoothReply(aRunnable, v, errorStr); } } else { @@ -2496,10 +2521,10 @@ BluetoothDBusService::Disconnect(const uint16_t aProfileId, return; } - // Currently, just fire success because Disconnect() doesn't fail, + // Currently, just fire success because Disconnect() doesn't fail, // but we still make aRunnable pass into this function for future // once Disconnect will fail. - nsString replyError; + nsAutoString replyError; BluetoothValue v = true; DispatchBluetoothReply(aRunnable, v, replyError); } @@ -2512,7 +2537,8 @@ BluetoothDBusService::IsConnected(const uint16_t aProfileId) if (aProfileId == BluetoothServiceClass::HANDSFREE || aProfileId == BluetoothServiceClass::HEADSET) { BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); - return hfp->GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED; + return (hfp->GetConnectionStatus() == + SocketConnectionStatus::SOCKET_CONNECTED); } else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) { BluetoothOppManager* opp = BluetoothOppManager::Get(); return opp->IsTransferring(); @@ -2550,7 +2576,7 @@ public: nsString address = GetAddressFromObjectPath(mObjectPath); BluetoothValue v; - nsString replyError; + nsAutoString replyError; BluetoothUnixSocketConnector* c = new BluetoothUnixSocketConnector(mType, mChannel, mAuth, mEncrypt); if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) { @@ -2631,18 +2657,19 @@ private: }; nsresult -BluetoothDBusService::GetSocketViaService(const nsAString& aObjectPath, - const nsAString& aService, - BluetoothSocketType aType, - bool aAuth, - bool aEncrypt, - mozilla::ipc::UnixSocketConsumer* aConsumer, - BluetoothReplyRunnable* aRunnable) +BluetoothDBusService::GetSocketViaService( + const nsAString& aObjectPath, + const nsAString& aService, + BluetoothSocketType aType, + bool aAuth, + bool aEncrypt, + mozilla::ipc::UnixSocketConsumer* aConsumer, + BluetoothReplyRunnable* aRunnable) { NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!"); if (!IsReady()) { BluetoothValue v; - nsString errorStr; + nsAutoString errorStr; errorStr.AssignLiteral("Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, v, errorStr); return NS_OK; @@ -2678,12 +2705,12 @@ BluetoothDBusService::GetScoSocket(const nsAString& aAddress, return NS_ERROR_FAILURE; } - nsString replyError; BluetoothUnixSocketConnector* c = new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1, aAuth, aEncrypt); if (!aConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(aAddress).get())) { + nsAutoString replyError; replyError.AssignLiteral("SocketConnectionError"); return NS_ERROR_FAILURE; } @@ -2705,7 +2732,7 @@ BluetoothDBusService::SendFile(const nsAString& aDeviceAddress, // it for future use. BluetoothOppManager* opp = BluetoothOppManager::Get(); BluetoothValue v = true; - nsString errorStr; + nsAutoString errorStr; if (!opp->SendFile(aBlobParent)) { errorStr.AssignLiteral("Calling SendFile() failed"); @@ -2726,7 +2753,7 @@ BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress, // it for future use. BluetoothOppManager* opp = BluetoothOppManager::Get(); BluetoothValue v = true; - nsString errorStr; + nsAutoString errorStr; if (!opp->StopSendingFile()) { errorStr.AssignLiteral("Calling StopSendingFile() failed"); @@ -2748,7 +2775,7 @@ BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress, // it for future use. BluetoothOppManager* opp = BluetoothOppManager::Get(); BluetoothValue v = true; - nsString errorStr; + nsAutoString errorStr; if (!opp->ConfirmReceivingFile(aConfirm)) { errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed"); @@ -2758,11 +2785,12 @@ BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress, } nsresult -BluetoothDBusService::ListenSocketViaService(int aChannel, - BluetoothSocketType aType, - bool aAuth, - bool aEncrypt, - mozilla::ipc::UnixSocketConsumer* aConsumer) +BluetoothDBusService::ListenSocketViaService( + int aChannel, + BluetoothSocketType aType, + bool aAuth, + bool aEncrypt, + mozilla::ipc::UnixSocketConsumer* aConsumer) { NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!"); diff --git a/dom/bluetooth/linux/BluetoothDBusService.h b/dom/bluetooth/linux/BluetoothDBusService.h index 7d474ecc843..42178912a24 100644 --- a/dom/bluetooth/linux/BluetoothDBusService.h +++ b/dom/bluetooth/linux/BluetoothDBusService.h @@ -35,18 +35,15 @@ public: virtual nsresult GetPairedDevicePropertiesInternal(const nsTArray& aDeviceAddresses, BluetoothReplyRunnable* aRunnable); - virtual nsresult StartDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable); + virtual nsresult StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable); - virtual nsresult StopDiscoveryInternal(const nsAString& aAdapterPath, - BluetoothReplyRunnable* aRunnable); + virtual nsresult StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable); virtual nsresult GetDevicePropertiesInternal(const BluetoothSignal& aSignal); virtual nsresult SetProperty(BluetoothObjectType aType, - const nsAString& aPath, const BluetoothNamedValue& aValue, BluetoothReplyRunnable* aRunnable); @@ -56,27 +53,23 @@ public: nsAString& aDevicePath); static bool - AddServiceRecords(const nsAString& aAdapterPath, - const char* serviceName, + AddServiceRecords(const char* serviceName, unsigned long long uuidMsb, unsigned long long uuidLsb, int channel); static bool - RemoveServiceRecords(const nsAString& aAdapterPath, - const char* serviceName, + RemoveServiceRecords(const char* serviceName, unsigned long long uuidMsb, unsigned long long uuidLsb, int channel); static bool - AddReservedServicesInternal(const nsAString& aAdapterPath, - const nsTArray& aServices, + AddReservedServicesInternal(const nsTArray& aServices, nsTArray& aServiceHandlesContainer); static bool - RemoveReservedServicesInternal(const nsAString& aAdapterPath, - const nsTArray& aServiceHandles); + RemoveReservedServicesInternal(const nsTArray& aServiceHandles); virtual nsresult GetScoSocket(const nsAString& aObjectPath, @@ -101,14 +94,12 @@ public: mozilla::ipc::UnixSocketConsumer* aConsumer); virtual nsresult - CreatePairedDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aDeviceAddress, + CreatePairedDeviceInternal(const nsAString& aDeviceAddress, int aTimeout, BluetoothReplyRunnable* aRunnable); virtual nsresult - RemoveDeviceInternal(const nsAString& aAdapterPath, - const nsAString& aDeviceObjectPath, + RemoveDeviceInternal(const nsAString& aDeviceObjectPath, BluetoothReplyRunnable* aRunnable); virtual bool @@ -128,11 +119,10 @@ public: BluetoothReplyRunnable* aRunnable); virtual nsresult - PrepareAdapterInternal(const nsAString& aPath); + PrepareAdapterInternal(); virtual void Connect(const nsAString& aDeviceAddress, - const nsAString& aAdapterPath, const uint16_t aProfileId, BluetoothReplyRunnable* aRunnable); @@ -161,10 +151,9 @@ private: const char* aInterface, void (*aCB)(DBusMessage *, void *), BluetoothReplyRunnable* aRunnable); - nsresult SendDiscoveryMessage(const nsAString& aAdapterPath, - const char* aMessageName, + nsresult SendDiscoveryMessage(const char* aMessageName, BluetoothReplyRunnable* aRunnable); - nsresult SendSetPropertyMessage(const nsString& aPath, const char* aInterface, + nsresult SendSetPropertyMessage(const char* aInterface, const BluetoothNamedValue& aValue, BluetoothReplyRunnable* aRunnable); From f583c0962812fc8dec4ec0af457c2fef71be242e Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Tue, 12 Mar 2013 11:27:59 +0800 Subject: [PATCH 047/202] Bug 849757 - Part 1: Add 'corporateLocked' and 'serviceProviderLocked' to cardState in IDL. r=vicamo. sr=sicking. --- .../interfaces/nsIDOMMobileConnection.idl | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/dom/network/interfaces/nsIDOMMobileConnection.idl b/dom/network/interfaces/nsIDOMMobileConnection.idl index aa06abd5115..602d31bcb13 100644 --- a/dom/network/interfaces/nsIDOMMobileConnection.idl +++ b/dom/network/interfaces/nsIDOMMobileConnection.idl @@ -30,7 +30,7 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget * Indicates the state of the device's ICC card. * * Possible values: null, 'unknown', 'absent', 'pinRequired', 'pukRequired', - * 'networkLocked', 'ready'. + * 'networkLocked', 'corporateLocked', 'serviceProviderLocked', 'ready'. */ readonly attribute DOMString cardState; @@ -131,17 +131,28 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget * unlockCardLock({lockType: "pin", * pin: "..."}); * - * (2) Network depersonalization. Unlocking the network control key (NCK). - * - * unlockCardLock({lockType: "nck", - * pin: "..."}); - * - * (3) Unlocking the PUK and supplying a new PIN: + * (2) Unlocking the PUK and supplying a new PIN: * * unlockCardLock({lockType: "puk", * puk: "...", * newPin: "..."}); * + * (3) Network depersonalization. Unlocking the network control key (NCK). + * + * unlockCardLock({lockType: "nck", + * pin: "..."}); + * + * (4) Corporate depersonalization. Unlocking the corporate control key (CCK). + * + * unlockCardLock({lockType: "cck", + * pin: "..."}); + * + * (5) Service Provider depersonalization. Unlocking the service provider + * control key (SPCK). + * + * unlockCardLock({lockType: "spck", + * pin: "..."}); + * * @return a nsIDOMDOMRequest. * The request's result will be an object containing * information about the unlock operation. From aaa9f9f061a339b7d603c5d47f5780be73a88a84 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Tue, 12 Mar 2013 11:37:58 +0800 Subject: [PATCH 048/202] Bug 849757 - Part 2: Add 'corporateLocked' and 'serviceProviderLocked' to cardState in RIL. r=vicamo --- dom/system/gonk/ril_consts.js | 27 ++++++++++++++++++++------- dom/system/gonk/ril_worker.js | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/dom/system/gonk/ril_consts.js b/dom/system/gonk/ril_consts.js index 0b741352ae9..839b11af8fd 100644 --- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -2219,13 +2219,26 @@ this.GECKO_RADIOSTATE_UNAVAILABLE = null; this.GECKO_RADIOSTATE_OFF = "off"; this.GECKO_RADIOSTATE_READY = "ready"; -this.GECKO_CARDSTATE_UNKNOWN = "unknown"; -this.GECKO_CARDSTATE_ABSENT = "absent"; -this.GECKO_CARDSTATE_PIN_REQUIRED = "pinRequired"; -this.GECKO_CARDSTATE_PUK_REQUIRED = "pukRequired"; -this.GECKO_CARDSTATE_NETWORK_LOCKED = "networkLocked"; -this.GECKO_CARDSTATE_NOT_READY = null; -this.GECKO_CARDSTATE_READY = "ready"; +this.GECKO_CARDSTATE_NOT_READY = null; +this.GECKO_CARDSTATE_UNKNOWN = "unknown"; +this.GECKO_CARDSTATE_ABSENT = "absent"; +this.GECKO_CARDSTATE_PIN_REQUIRED = "pinRequired"; +this.GECKO_CARDSTATE_PUK_REQUIRED = "pukRequired"; +this.GECKO_CARDSTATE_NETWORK_LOCKED = "networkLocked"; +this.GECKO_CARDSTATE_CORPORATE_LOCKED = "corporateLocked"; +this.GECKO_CARDSTATE_SERVICE_PROVIDER_LOCKED = "serviceProviderLocked"; +this.GECKO_CARDSTATE_READY = "ready"; + +// See ril.h RIL_PersoSubstate +this.PERSONSUBSTATE = {}; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_UNKNOWN] = GECKO_CARDSTATE_UNKNOWN; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_IN_PROGRESS] = "inProgress"; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_READY] = GECKO_CARDSTATE_READY; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_NETWORK] = GECKO_CARDSTATE_NETWORK_LOCKED; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_NETWORK_SUBSET] = "networkSubsetLocked"; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_CORPORATE] = GECKO_CARDSTATE_CORPORATE_LOCKED; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER] = GECKO_CARDSTATE_SERVICE_PROVIDER_LOCKED; +PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_SIM] = "simPersonalizationLock"; this.GECKO_NETWORK_SELECTION_UNKNOWN = null; this.GECKO_NETWORK_SELECTION_AUTOMATIC = "automatic"; diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index ea296bec714..ab1d731e830 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -2858,7 +2858,7 @@ let RIL = { newCardState = GECKO_CARDSTATE_PUK_REQUIRED; break; case CARD_APPSTATE_SUBSCRIPTION_PERSO: - newCardState = GECKO_CARDSTATE_NETWORK_LOCKED; + newCardState = PERSONSUBSTATE[app.perso_substate]; break; case CARD_APPSTATE_READY: newCardState = GECKO_CARDSTATE_READY; From 575ad115cc000b1aafe5a64c6093ad37a12dd466 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Tue, 12 Mar 2013 14:43:01 +0800 Subject: [PATCH 049/202] Bug 849757 - Part 3: spck and cck in RIL. r=vicamo --- dom/system/gonk/ril_worker.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index ab1d731e830..5a08327cd63 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -941,7 +941,7 @@ let RIL = { Buf.simpleRequest(REQUEST_GET_SIM_STATUS); }, - /** + /** * Helper function for unlocking ICC locks. */ iccUnlockCardLock: function iccUnlockCardLock(options) { @@ -962,6 +962,14 @@ let RIL = { options.type = CARD_PERSOSUBSTATE_SIM_NETWORK; this.enterDepersonalization(options); break; + case "cck": + options.type = CARD_PERSOSUBSTATE_SIM_CORPORATE; + this.enterDepersonalization(options); + break; + case "spck": + options.type = CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER; + this.enterDepersonalization(options); + break; default: options.errorMsg = "Unsupported Card Lock."; options.success = false; From 3056441d1c5a57c5e020cb6ce7edb11495927035 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Tue, 12 Mar 2013 15:06:48 +0800 Subject: [PATCH 050/202] Bug 849757 - Part 4: xpcshell tests. r=vicamo --- dom/system/gonk/tests/test_ril_worker_icc.js | 80 ++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/dom/system/gonk/tests/test_ril_worker_icc.js b/dom/system/gonk/tests/test_ril_worker_icc.js index bbf58c630c6..0b844d2831d 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc.js +++ b/dom/system/gonk/tests/test_ril_worker_icc.js @@ -2080,3 +2080,83 @@ add_test(function test_update_icc_contact() { run_next_test(); }); +/** + * Verify cardState 'corporateLocked'. + */ +add_test(function test_card_state_corporateLocked() { + let worker = newUint8Worker(); + let ril = worker.RIL; + let iccStatus = { + gsmUmtsSubscriptionAppIndex: 0, + apps: [ + { + app_state: CARD_APPSTATE_SUBSCRIPTION_PERSO, + perso_substate: CARD_PERSOSUBSTATE_SIM_CORPORATE + }], + }; + + ril._processICCStatus(iccStatus); + do_check_eq(ril.cardState, GECKO_CARDSTATE_CORPORATE_LOCKED); + + run_next_test(); +}); + +/** + * Verify cardState 'serviceProviderLocked'. + */ +add_test(function test_card_state_serviceProviderLocked() { + let worker = newUint8Worker(); + let ril = worker.RIL; + let iccStatus = { + gsmUmtsSubscriptionAppIndex: 0, + apps: [ + { + app_state: CARD_APPSTATE_SUBSCRIPTION_PERSO, + perso_substate: CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER + }], + }; + + ril._processICCStatus(iccStatus); + do_check_eq(ril.cardState, GECKO_CARDSTATE_SERVICE_PROVIDER_LOCKED); + + run_next_test(); +}); + +/** + * Verify iccUnlockCardLock with lockType is "cck" and "spck". + */ +add_test(function test_unlock_card_lock_corporateLocked() { + let worker = newUint8Worker(); + let ril = worker.RIL; + let buf = worker.Buf; + const pin = "12345678"; + + function do_test(aLock, aPin) { + buf.sendParcel = function fakeSendParcel () { + // Request Type. + do_check_eq(this.readUint32(), REQUEST_ENTER_NETWORK_DEPERSONALIZATION_CODE); + + // Token : we don't care + this.readUint32(); + + let lockType = aLock === "cck" ? + CARD_PERSOSUBSTATE_SIM_CORPORATE : + CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER; + + // Lock Type + do_check_eq(this.readUint32(), lockType); + + // Pin. + do_check_eq(this.readString(), aPin); + }; + + ril.iccUnlockCardLock({lockType: aLock, + pin: aPin}); + } + + do_test("cck", pin); + do_test("spck", pin); + + run_next_test(); +}); + From 12b037eded2667da8065abc43c5dc8b500c2f929 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Fri, 15 Mar 2013 15:23:18 +0800 Subject: [PATCH 051/202] Bug 849758 - [b2g-ril] SIM PIN requested after booting/rebooting with airplane mode on. r=vicamo. --- dom/system/gonk/ril_worker.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index 5a08327cd63..2e220b2bc50 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -4547,7 +4547,15 @@ RIL[REQUEST_OPERATOR] = function REQUEST_OPERATOR(length, options) { if (DEBUG) debug("Operator: " + operatorData); this._processOperator(operatorData); }; -RIL[REQUEST_RADIO_POWER] = null; +RIL[REQUEST_RADIO_POWER] = function REQUEST_RADIO_POWER(length, options) { + if (options.rilRequestError) { + return; + } + + if (this._isInitialRadioState) { + this._isInitialRadioState = false; + } +}; RIL[REQUEST_DTMF] = null; RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, options) { this._processSmsSendResult(length, options); @@ -5069,11 +5077,10 @@ RIL[UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED] = function UNSOLICITED_RESPONSE_RA // Ensure radio state at boot time. if (this._isInitialRadioState) { - this._isInitialRadioState = false; - if (radioState != RADIO_STATE_OFF) { - this.setRadioPower({on: false}); - return; - } + // Even radioState is RADIO_STATE_OFF, we still have to maually turn radio off, + // otherwise REQUEST_GET_SIM_STATUS will still report CARD_STATE_PRESENT. + this.setRadioPower({on: false}); + return; } let newState; From 989b3e679d3349d97305c7435341a3c7a2410e40 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Fri, 15 Mar 2013 04:20:30 -0400 Subject: [PATCH 052/202] Bug 832155 bustage fix --- content/svg/content/src/nsSVGFilters.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/content/svg/content/src/nsSVGFilters.cpp b/content/svg/content/src/nsSVGFilters.cpp index 61a730db5d2..3c18994d63a 100644 --- a/content/svg/content/src/nsSVGFilters.cpp +++ b/content/svg/content/src/nsSVGFilters.cpp @@ -36,6 +36,7 @@ #include "nsContentUtils.h" #include "mozilla/dom/SVGAnimatedLength.h" #include "mozilla/dom/SVGComponentTransferFunctionElement.h" +#include "mozilla/dom/SVGFEDistantLightElement.h" #include "mozilla/dom/SVGFEFuncAElementBinding.h" #include "mozilla/dom/SVGFEFuncBElementBinding.h" #include "mozilla/dom/SVGFEFuncGElementBinding.h" @@ -3979,7 +3980,7 @@ nsSVGFELightingElement::Filter(nsSVGFilterInstance *instance, if (!info.mTarget) return NS_ERROR_FAILURE; - nsCOMPtr distantLight; + SVGFEDistantLightElement* distantLight; SVGFEPointLightElement* pointLight; nsCOMPtr spotLight; @@ -3993,7 +3994,8 @@ nsSVGFELightingElement::Filter(nsSVGFilterInstance *instance, for (nsCOMPtr child = nsINode::GetFirstChild(); child; child = child->GetNextSibling()) { - distantLight = do_QueryInterface(child); + distantLight = child->IsSVG(nsGkAtoms::feDistantLight) ? + static_cast(child.get()) : nullptr; pointLight = child->IsSVG(nsGkAtoms::fePointLight) ? static_cast(child.get()) : nullptr; spotLight = do_QueryInterface(child); @@ -4009,10 +4011,9 @@ nsSVGFELightingElement::Filter(nsSVGFilterInstance *instance, float L[3]; if (distantLight) { float azimuth, elevation; - static_cast - (distantLight.get())->GetAnimatedNumberValues(&azimuth, - &elevation, - nullptr); + distantLight->GetAnimatedNumberValues(&azimuth, + &elevation, + nullptr); L[0] = cos(azimuth * radPerDeg) * cos(elevation * radPerDeg); L[1] = sin(azimuth * radPerDeg) * cos(elevation * radPerDeg); L[2] = sin(elevation * radPerDeg); From 3727f837edc61c945ce01a2e378cbe59b0d837d5 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Fri, 15 Mar 2013 16:58:58 +0800 Subject: [PATCH 053/202] Backout changeset f445e0cbba3b(Bug 849758) --- dom/system/gonk/ril_worker.js | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index 2e220b2bc50..5a08327cd63 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -4547,15 +4547,7 @@ RIL[REQUEST_OPERATOR] = function REQUEST_OPERATOR(length, options) { if (DEBUG) debug("Operator: " + operatorData); this._processOperator(operatorData); }; -RIL[REQUEST_RADIO_POWER] = function REQUEST_RADIO_POWER(length, options) { - if (options.rilRequestError) { - return; - } - - if (this._isInitialRadioState) { - this._isInitialRadioState = false; - } -}; +RIL[REQUEST_RADIO_POWER] = null; RIL[REQUEST_DTMF] = null; RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, options) { this._processSmsSendResult(length, options); @@ -5077,10 +5069,11 @@ RIL[UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED] = function UNSOLICITED_RESPONSE_RA // Ensure radio state at boot time. if (this._isInitialRadioState) { - // Even radioState is RADIO_STATE_OFF, we still have to maually turn radio off, - // otherwise REQUEST_GET_SIM_STATUS will still report CARD_STATE_PRESENT. - this.setRadioPower({on: false}); - return; + this._isInitialRadioState = false; + if (radioState != RADIO_STATE_OFF) { + this.setRadioPower({on: false}); + return; + } } let newState; From 878e9a226ec8f79434caf85de4c7713b5c99dd7a Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Fri, 15 Mar 2013 18:03:16 +0900 Subject: [PATCH 054/202] Bug 829952 Reimplement system scroll speed override stuff for D3E WheelEvent feedback=avih, r=smaug+roc, sr=roc --- content/events/src/nsEventStateManager.cpp | 39 +++++----------- widget/nsIWidget.h | 25 +++++----- widget/windows/nsWindow.cpp | 44 +++++++++++------- widget/windows/nsWindow.h | 5 +- widget/xpwidgets/nsBaseWidget.cpp | 53 +++++++++++++--------- widget/xpwidgets/nsBaseWidget.h | 5 +- 6 files changed, 92 insertions(+), 79 deletions(-) diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index cdfbcc6e5a6..7c13a5ab81b 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -646,21 +646,16 @@ nsMouseWheelTransaction::OverrideSystemScrollSpeed(widget::WheelEvent* aEvent) MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction"); MOZ_ASSERT(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE); - DeltaValues result(aEvent); - // If the event doesn't scroll to both X and Y, we don't need to do anything - // here. And also, if the event indicates the device supports high - // resolution scroll, we shouldn't need to override it. - if ((!aEvent->lineOrPageDeltaX && !aEvent->lineOrPageDeltaY) || - (static_cast(aEvent->lineOrPageDeltaX) != aEvent->deltaX) || - (static_cast(aEvent->lineOrPageDeltaY) != aEvent->deltaY)) { - return result; + // here. + if (!aEvent->deltaX && !aEvent->deltaY) { + return DeltaValues(aEvent); } // We shouldn't override the scrolling speed on non root scroll frame. if (sTargetFrame != sTargetFrame->PresContext()->PresShell()->GetRootScrollFrame()) { - return result; + return DeltaValues(aEvent); } // Compute the overridden speed to nsIWidget. The widget can check the @@ -668,25 +663,13 @@ nsMouseWheelTransaction::OverrideSystemScrollSpeed(widget::WheelEvent* aEvent) // the system settings of the mouse wheel scrolling or not), and can limit // the speed for preventing the unexpected high speed scrolling. nsCOMPtr widget(sTargetFrame->GetNearestWidget()); - NS_ENSURE_TRUE(widget, result); - int32_t overriddenDeltaX = 0, overriddenDeltaY = 0; - if (aEvent->lineOrPageDeltaX) { - nsresult rv = - widget->OverrideSystemMouseScrollSpeed(aEvent->lineOrPageDeltaX, - true, overriddenDeltaX); - if (NS_FAILED(rv)) { - return result; - } - } - if (aEvent->lineOrPageDeltaY) { - nsresult rv = - widget->OverrideSystemMouseScrollSpeed(aEvent->lineOrPageDeltaY, - false, overriddenDeltaY); - if (NS_FAILED(rv)) { - return result; - } - } - return DeltaValues(overriddenDeltaX, overriddenDeltaY); + NS_ENSURE_TRUE(widget, DeltaValues(aEvent)); + DeltaValues overriddenDeltaValues(0.0, 0.0); + nsresult rv = + widget->OverrideSystemMouseScrollSpeed(aEvent->deltaX, aEvent->deltaY, + overriddenDeltaValues.deltaX, + overriddenDeltaValues.deltaY); + return NS_FAILED(rv) ? DeltaValues(aEvent) : overriddenDeltaValues; } /******************************************************************/ diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index fad67ef0651..06e88347162 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -92,8 +92,8 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event); #endif #define NS_IWIDGET_IID \ - { 0x48568C1E, 0xAF56, 0x4F73, \ - { 0x94, 0x6D, 0xAA, 0x43, 0xD8, 0x96, 0x78, 0x6B } } + { 0xdaac8d94, 0x14f3, 0x4bc4, \ + { 0xa8, 0xc, 0xf0, 0xe6, 0x46, 0x1e, 0xad, 0x40 } } /* * Window shadow styles @@ -1553,16 +1553,19 @@ class nsIWidget : public nsISupports { * actions. And also this isn't called when the user doesn't use the * system wheel speed settings. * - * @param aOriginalDelta The delta value of the current mouse wheel - * scrolling event. - * @param aIsHorizontal If TRUE, the scrolling direction is horizontal. - * Otherwise, it's vertical. - * @param aOverriddenDelta The overridden mouse scrolling speed. This value - * may be same as aOriginalDelta. + * @param aOriginalDeltaX The X delta value of the current mouse wheel + * scrolling event. + * @param aOriginalDeltaX The Y delta value of the current mouse wheel + * scrolling event. + * @param aOverriddenDeltaX The overridden mouse scrolling speed along X + * axis. This value may be same as aOriginalDeltaX. + * @param aOverriddenDeltaY The overridden mouse scrolling speed along Y + * axis. This value may be same as aOriginalDeltaY. */ - NS_IMETHOD OverrideSystemMouseScrollSpeed(int32_t aOriginalDelta, - bool aIsHorizontal, - int32_t &aOverriddenDelta) = 0; + NS_IMETHOD OverrideSystemMouseScrollSpeed(double aOriginalDeltaX, + double aOriginalDeltaY, + double& aOverriddenDeltaX, + double& aOverriddenDeltaY) = 0; /** * Return true if this process shouldn't use platform widgets, and diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index b8440830cc9..8ae839f8f85 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -3426,26 +3426,32 @@ nsWindow::OnDefaultButtonLoaded(const nsIntRect &aButtonRect) } NS_IMETHODIMP -nsWindow::OverrideSystemMouseScrollSpeed(int32_t aOriginalDelta, - bool aIsHorizontal, - int32_t &aOverriddenDelta) +nsWindow::OverrideSystemMouseScrollSpeed(double aOriginalDeltaX, + double aOriginalDeltaY, + double& aOverriddenDeltaX, + double& aOverriddenDeltaY) { // The default vertical and horizontal scrolling speed is 3, this is defined // on the document of SystemParametersInfo in MSDN. const uint32_t kSystemDefaultScrollingSpeed = 3; - int32_t absOriginDelta = DeprecatedAbs(aOriginalDelta); + double absOriginDeltaX = Abs(aOriginalDeltaX); + double absOriginDeltaY = Abs(aOriginalDeltaY); // Compute the simple overridden speed. - int32_t absComputedOverriddenDelta; + double absComputedOverriddenDeltaX, absComputedOverriddenDeltaY; nsresult rv = - nsBaseWidget::OverrideSystemMouseScrollSpeed(absOriginDelta, aIsHorizontal, - absComputedOverriddenDelta); + nsBaseWidget::OverrideSystemMouseScrollSpeed(absOriginDeltaX, + absOriginDeltaY, + absComputedOverriddenDeltaX, + absComputedOverriddenDeltaY); NS_ENSURE_SUCCESS(rv, rv); - aOverriddenDelta = aOriginalDelta; + aOverriddenDeltaX = aOriginalDeltaX; + aOverriddenDeltaY = aOriginalDeltaY; - if (absComputedOverriddenDelta == absOriginDelta) { + if (absComputedOverriddenDeltaX == absOriginDeltaX && + absComputedOverriddenDeltaY == absOriginDeltaY) { // We don't override now. return NS_OK; } @@ -3479,23 +3485,29 @@ nsWindow::OverrideSystemMouseScrollSpeed(int32_t aOriginalDelta, // driver might accelerate the scrolling speed already. If so, we shouldn't // override the scrolling speed for preventing the unexpected high speed // scrolling. - int32_t absDeltaLimit; + double absDeltaLimitX, absDeltaLimitY; rv = nsBaseWidget::OverrideSystemMouseScrollSpeed(kSystemDefaultScrollingSpeed, - aIsHorizontal, absDeltaLimit); + kSystemDefaultScrollingSpeed, + absDeltaLimitX, + absDeltaLimitY); NS_ENSURE_SUCCESS(rv, rv); // If the given delta is larger than our computed limitation value, the delta // was accelerated by the mouse driver. So, we should do nothing here. - if (absDeltaLimit <= absOriginDelta) { + if (absDeltaLimitX <= absOriginDeltaX || absDeltaLimitY <= absOriginDeltaY) { return NS_OK; } - absComputedOverriddenDelta = - std::min(absComputedOverriddenDelta, absDeltaLimit); + aOverriddenDeltaX = std::min(absComputedOverriddenDeltaX, absDeltaLimitX); + aOverriddenDeltaY = std::min(absComputedOverriddenDeltaY, absDeltaLimitY); - aOverriddenDelta = (aOriginalDelta > 0) ? absComputedOverriddenDelta : - -absComputedOverriddenDelta; + if (aOriginalDeltaX < 0) { + aOverriddenDeltaX *= -1; + } + if (aOriginalDeltaY < 0) { + aOverriddenDeltaY *= -1; + } return NS_OK; } diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 216c7a3cad3..a717819ccca 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -142,7 +142,10 @@ public: bool* aAllowRetaining = nullptr); gfxASurface *GetThebesSurface(); NS_IMETHOD OnDefaultButtonLoaded(const nsIntRect &aButtonRect); - NS_IMETHOD OverrideSystemMouseScrollSpeed(int32_t aOriginalDelta, bool aIsHorizontal, int32_t &aOverriddenDelta); + NS_IMETHOD OverrideSystemMouseScrollSpeed(double aOriginalDeltaX, + double aOriginalDeltaY, + double& aOverriddenDeltaX, + double& aOverriddenDeltaY); virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode, diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index b178c7a043d..ece3fe512fa 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -1198,36 +1198,45 @@ NS_METHOD nsBaseWidget::UnregisterTouchWindow() } NS_IMETHODIMP -nsBaseWidget::OverrideSystemMouseScrollSpeed(int32_t aOriginalDelta, - bool aIsHorizontal, - int32_t &aOverriddenDelta) +nsBaseWidget::OverrideSystemMouseScrollSpeed(double aOriginalDeltaX, + double aOriginalDeltaY, + double& aOverriddenDeltaX, + double& aOverriddenDeltaY) { - aOverriddenDelta = aOriginalDelta; + aOverriddenDeltaX = aOriginalDeltaX; + aOverriddenDeltaY = aOriginalDeltaY; - const char* kPrefNameOverrideEnabled = - "mousewheel.system_scroll_override_on_root_content.enabled"; - bool isOverrideEnabled = - Preferences::GetBool(kPrefNameOverrideEnabled, false); - if (!isOverrideEnabled) { + static bool sInitialized = false; + static bool sIsOverrideEnabled = false; + static int32_t sIntFactorX = 0; + static int32_t sIntFactorY = 0; + + if (!sInitialized) { + Preferences::AddBoolVarCache(&sIsOverrideEnabled, + "mousewheel.system_scroll_override_on_root_content.enabled", false); + Preferences::AddIntVarCache(&sIntFactorX, + "mousewheel.system_scroll_override_on_root_content.horizontal.factor", 0); + Preferences::AddIntVarCache(&sIntFactorY, + "mousewheel.system_scroll_override_on_root_content.vertical.factor", 0); + sIntFactorX = std::max(sIntFactorX, 0); + sIntFactorY = std::max(sIntFactorY, 0); + sInitialized = true; + } + + if (!sIsOverrideEnabled) { return NS_OK; } - nsAutoCString factorPrefName( - "mousewheel.system_scroll_override_on_root_content."); - if (aIsHorizontal) { - factorPrefName.AppendLiteral("horizontal."); - } else { - factorPrefName.AppendLiteral("vertical."); - } - factorPrefName.AppendLiteral("factor"); - int32_t iFactor = Preferences::GetInt(factorPrefName.get(), 0); // The pref value must be larger than 100, otherwise, we don't override the // delta value. - if (iFactor <= 100) { - return NS_OK; + if (sIntFactorX > 100) { + double factor = static_cast(sIntFactorX) / 100; + aOverriddenDeltaX *= factor; + } + if (sIntFactorY > 100) { + double factor = static_cast(sIntFactorY) / 100; + aOverriddenDeltaY *= factor; } - double factor = (double)iFactor / 100; - aOverriddenDelta = int32_t(NS_round((double)aOriginalDelta * factor)); return NS_OK; } diff --git a/widget/xpwidgets/nsBaseWidget.h b/widget/xpwidgets/nsBaseWidget.h index e23acd050e1..ea33c151bbd 100644 --- a/widget/xpwidgets/nsBaseWidget.h +++ b/widget/xpwidgets/nsBaseWidget.h @@ -148,7 +148,10 @@ public: NS_IMETHOD NotifyIMEOfTextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; } virtual nsIMEUpdatePreference GetIMEUpdatePreference() { return nsIMEUpdatePreference(false, false); } NS_IMETHOD OnDefaultButtonLoaded(const nsIntRect &aButtonRect) { return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD OverrideSystemMouseScrollSpeed(int32_t aOriginalDelta, bool aIsHorizontal, int32_t &aOverriddenDelta); + NS_IMETHOD OverrideSystemMouseScrollSpeed(double aOriginalDeltaX, + double aOriginalDeltaY, + double& aOverriddenDeltaX, + double& aOverriddenDeltaY); virtual already_AddRefed CreateChild(const nsIntRect &aRect, nsDeviceContext *aContext, From f96e4330d89cf82b025b8e3c06b956d0b021b2e0 Mon Sep 17 00:00:00 2001 From: Frank Yan Date: Fri, 15 Mar 2013 02:00:35 -0700 Subject: [PATCH 055/202] Bug 851011 - Remove unused feed handlers from mobile/. r=mfinkle --- mobile/locales/en-US/chrome/region.properties | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mobile/locales/en-US/chrome/region.properties b/mobile/locales/en-US/chrome/region.properties index 0fee1d9be88..518944d6c16 100644 --- a/mobile/locales/en-US/chrome/region.properties +++ b/mobile/locales/en-US/chrome/region.properties @@ -8,13 +8,6 @@ browser.search.defaultenginename=Google # Search engine order (order displayed in the search bar dropdown)s browser.search.order.1=Google -# This is the default set of web based feed handlers shown in the reader -# selection UI -browser.contentHandlers.types.0.title=My Yahoo -browser.contentHandlers.types.0.uri=http://add.my.yahoo.com/rss?url=%s -browser.contentHandlers.types.1.title=Google -browser.contentHandlers.types.1.uri=http://fusion.google.com/add?feedurl=%s - # Keyword URL (for location bar searches) keyword.URL=https://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q= From f7c6a3dded7e848e9f14592cd63184660bbb20c7 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 15 Mar 2013 09:04:54 +0000 Subject: [PATCH 056/202] Bug 842179 - Keep the thumb for within its content box. r=dholbert. --- layout/forms/nsRangeFrame.cpp | 174 +++++++++++++++++++--------------- layout/forms/nsRangeFrame.h | 3 + 2 files changed, 100 insertions(+), 77 deletions(-) diff --git a/layout/forms/nsRangeFrame.cpp b/layout/forms/nsRangeFrame.cpp index 7ffc361d2a2..1e66416e48b 100644 --- a/layout/forms/nsRangeFrame.cpp +++ b/layout/forms/nsRangeFrame.cpp @@ -242,63 +242,26 @@ nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext, nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (thumbFrame) { // display:none? - - // Position the thumb: - // The idea here is that we want to position the thumb so that the center - // of the thumb is on an imaginary line drawn from the middle of one edge - // of the range frame's content box to the middle of the opposite edge of - // its content box (the opposite edges being the left/right edge if the - // range is horizontal, or else the top/bottom edges if the range is - // vertical). How far along this line the center of the thumb is placed - // depends on the value of the range. - - nsSize frameSizeOverride(aDesiredSize.width, aDesiredSize.height); - bool isHorizontal = IsHorizontal(&frameSizeOverride); - - double valueAsFraction = GetValueAsFractionOfRange(); - MOZ_ASSERT(valueAsFraction >= 0.0 && valueAsFraction <= 1.0); - nsHTMLReflowState thumbReflowState(aPresContext, aReflowState, thumbFrame, nsSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE)); - // Find the x/y position of the thumb frame such that it will be positioned - // as described above. These coordinates are with respect to the - // nsRangeFrame's border-box. - nscoord thumbX, thumbY; - - if (isHorizontal) { - thumbX = NSToCoordRound(rangeFrameContentBoxWidth * valueAsFraction); - if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { - thumbX = rangeFrameContentBoxWidth - thumbX; - } - thumbY = rangeFrameContentBoxHeight / 2; - } else { - thumbX = rangeFrameContentBoxWidth / 2; - // For vertical range zero is at the bottom, so subtract from height: - thumbY = rangeFrameContentBoxHeight - - NSToCoordRound(rangeFrameContentBoxHeight * valueAsFraction); - } - - thumbX -= thumbReflowState.mComputedBorderPadding.left + - thumbReflowState.ComputedWidth() / 2; - thumbY -= thumbReflowState.mComputedBorderPadding.top + - thumbReflowState.ComputedHeight() / 2; - - // Make relative to our border box instead of our content box: - thumbX += aReflowState.mComputedBorderPadding.left; - thumbY += aReflowState.mComputedBorderPadding.top; + // Where we position of the thumb depends on its size, so we first reflow + // the thumb at {0,0} to obtain its size, then position it afterwards. nsReflowStatus frameStatus = NS_FRAME_COMPLETE; nsHTMLReflowMetrics thumbDesiredSize; nsresult rv = ReflowChild(thumbFrame, aPresContext, thumbDesiredSize, - thumbReflowState, thumbX, thumbY, 0, frameStatus); + thumbReflowState, 0, 0, 0, frameStatus); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); rv = FinishReflowChild(thumbFrame, aPresContext, &thumbReflowState, - thumbDesiredSize, thumbX, thumbY, 0); + thumbDesiredSize, 0, 0, 0); NS_ENSURE_SUCCESS(rv, rv); + + DoUpdateThumbPosition(thumbFrame, nsSize(aDesiredSize.width, + aDesiredSize.height)); } return NS_OK; @@ -348,34 +311,57 @@ nsRangeFrame::GetValueAtEventPoint(nsGUIEvent* aEvent) MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(minimum) && MOZ_DOUBLE_IS_FINITE(maximum), "type=range should have a default maximum/minimum"); - nsRect contentRect = GetContentRectRelativeToSelf(); - if (maximum <= minimum || - (IsHorizontal() && contentRect.width <= 0) || - (!IsHorizontal() && contentRect.height <= 0)) { + if (maximum <= minimum) { return minimum; } - double range = maximum - minimum; - nsIntPoint point; + nsIntPoint absPoint; if (aEvent->eventStructType == NS_TOUCH_EVENT) { MOZ_ASSERT(static_cast(aEvent)->touches.Length() == 1, "Unexpected number of touches"); - point = static_cast(aEvent)->touches[0]->mRefPoint; + absPoint = static_cast(aEvent)->touches[0]->mRefPoint; } else { - point = aEvent->refPoint; + absPoint = aEvent->refPoint; + } + nsPoint point = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, absPoint, this); + + nsRect rangeContentRect = GetContentRectRelativeToSelf(); + nsSize thumbSize; + nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); + if (thumbFrame) { // diplay:none? + thumbSize = thumbFrame->GetSize(); } - nsMargin borderAndPadding = GetUsedBorderAndPadding(); - nsPoint contentPoint = - nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, point, this) - - nsPoint(borderAndPadding.left, borderAndPadding.top); - contentPoint.x = mozilla::clamped(contentPoint.x, 0, contentRect.width); - contentPoint.y = mozilla::clamped(contentPoint.y, 0, contentRect.height); - if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { - return maximum - (double(contentPoint.x) / double(contentRect.width)) * range; + double fraction; + if (IsHorizontal()) { + nscoord traversableDistance = rangeContentRect.width - thumbSize.width; + if (traversableDistance <= 0) { + return minimum; + } + nscoord posAtStart = rangeContentRect.x + thumbSize.width/2; + nscoord posAtEnd = posAtStart + traversableDistance; + nscoord posOfPoint = mozilla::clamped(point.x, posAtStart, posAtEnd); + fraction = (posOfPoint - posAtStart) / double(traversableDistance); + if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { + fraction = 1.0 - fraction; + } + } else { + nscoord traversableDistance = rangeContentRect.height - thumbSize.height; + if (traversableDistance <= 0) { + return minimum; + } + nscoord posAtStart = rangeContentRect.y + thumbSize.height/2; + nscoord posAtEnd = posAtStart + traversableDistance; + nscoord posOfPoint = mozilla::clamped(point.y, posAtStart, posAtEnd); + // For a vertical range, the top (posAtStart) is the highest value, so we + // subtract the fraction from 1.0 to get that polarity correct. + fraction = 1.0 - (posOfPoint - posAtStart) / double(traversableDistance); } - return minimum + (double(contentPoint.x) / double(contentRect.width)) * range; + + MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0); + return minimum + fraction * range; } void @@ -388,25 +374,59 @@ nsRangeFrame::UpdateThumbPositionForValueChange() if (!thumbFrame) { return; // diplay:none? } - // TODO in bug 842179 - factor out duplication here and in Reflow. - double fraction = GetValueAsFractionOfRange(); - nsRect contentRect = GetContentRectRelativeToSelf(); - nsMargin borderAndPadding = GetUsedBorderAndPadding(); - nsSize thumbSize = thumbFrame->GetSize(); - nsPoint newPosition(borderAndPadding.left, borderAndPadding.top); - if (IsHorizontal()) { - newPosition += nsPoint(NSToCoordRound(fraction * contentRect.width) - - thumbSize.width/2, - (contentRect.height - thumbSize.height)/2); - } else { - newPosition += nsPoint((contentRect.width - thumbSize.width)/2, - NSToCoordRound(fraction * contentRect.height) - - thumbSize.height/2); - } - thumbFrame->SetPosition(newPosition); + DoUpdateThumbPosition(thumbFrame, GetSize()); SchedulePaint(); } +void +nsRangeFrame::DoUpdateThumbPosition(nsIFrame* aThumbFrame, + const nsSize& aRangeSize) +{ + MOZ_ASSERT(aThumbFrame); + + // The idea here is that we want to position the thumb so that the center + // of the thumb is on an imaginary line drawn from the middle of one edge + // of the range frame's content box to the middle of the opposite edge of + // its content box (the opposite edges being the left/right edge if the + // range is horizontal, or else the top/bottom edges if the range is + // vertical). How far along this line the center of the thumb is placed + // depends on the value of the range. + + nsMargin borderAndPadding = GetUsedBorderAndPadding(); + nsPoint newPosition(borderAndPadding.left, borderAndPadding.top); + + nsSize rangeContentBoxSize(aRangeSize); + rangeContentBoxSize.width -= borderAndPadding.LeftRight(); + rangeContentBoxSize.height -= borderAndPadding.TopBottom(); + + nsSize thumbSize = aThumbFrame->GetSize(); + double fraction = GetValueAsFractionOfRange(); + MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0); + + // We are called under Reflow, so we need to pass IsHorizontal a valid rect. + nsSize frameSizeOverride(aRangeSize.width, aRangeSize.height); + if (IsHorizontal(&frameSizeOverride)) { + if (thumbSize.width < rangeContentBoxSize.width) { + nscoord traversableDistance = + rangeContentBoxSize.width - thumbSize.width; + if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { + newPosition.x += NSToCoordRound((1.0 - fraction) * traversableDistance); + } else { + newPosition.x += NSToCoordRound(fraction * traversableDistance); + } + newPosition.y += (rangeContentBoxSize.height - thumbSize.height)/2; + } + } else { + if (thumbSize.height < rangeContentBoxSize.height) { + nscoord traversableDistance = + rangeContentBoxSize.height - thumbSize.height; + newPosition.x += (rangeContentBoxSize.width - thumbSize.width)/2; + newPosition.y += NSToCoordRound((1.0 - fraction) * traversableDistance); + } + } + aThumbFrame->SetPosition(newPosition); +} + NS_IMETHODIMP nsRangeFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/forms/nsRangeFrame.h b/layout/forms/nsRangeFrame.h index 442a70aa667..ed35b9db7a6 100644 --- a/layout/forms/nsRangeFrame.h +++ b/layout/forms/nsRangeFrame.h @@ -106,6 +106,9 @@ private: nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState); + void DoUpdateThumbPosition(nsIFrame* aThumbFrame, + const nsSize& aRangeSize); + /** * Returns the input element's value as a fraction of the difference between * the input's minimum and its maximum (i.e. returns 0.0 when the value is From 46cc5613aa2efec24e73fd75d65e8c9c7d9bf0b2 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 15 Mar 2013 09:15:18 +0000 Subject: [PATCH 057/202] Bug 841948 - Flip the pref to enable on Nightly and Aurora. r=mounir. --- modules/libpref/src/init/all.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 9b277ded42b..e53f8c5241c 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -719,7 +719,7 @@ pref("dom.xbl_scopes", true); pref("dom.experimental_forms", false); // Don't enable yet: -pref("dom.experimental_forms_range", false); +pref("dom.experimental_forms_range", true); // Allocation Threshold for Workers pref("dom.workers.mem.gc_allocation_threshold_mb", 30); From 925dba12111353d314e7616f72e3274118b870d4 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Thu, 14 Mar 2013 23:16:03 +0200 Subject: [PATCH 058/202] Bug 847589 - Paris binding for AnimationEvent, r=Ms2ger --HG-- extra : rebase_source : 5987a31b2700481c17b0f430cd66c6e1bb7881e5 --- content/events/src/nsDOMAnimationEvent.cpp | 3 ++- content/events/src/nsDOMAnimationEvent.h | 24 ++++++++++++++++++++++ dom/bindings/Bindings.conf | 4 ++++ dom/webidl/AnimationEvent.webidl | 24 ++++++++++++++++++++++ dom/webidl/WebIDL.mk | 1 + 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 dom/webidl/AnimationEvent.webidl diff --git a/content/events/src/nsDOMAnimationEvent.cpp b/content/events/src/nsDOMAnimationEvent.cpp index 5a9e69a5ba5..74542d8f5e7 100644 --- a/content/events/src/nsDOMAnimationEvent.cpp +++ b/content/events/src/nsDOMAnimationEvent.cpp @@ -24,6 +24,7 @@ nsDOMAnimationEvent::nsDOMAnimationEvent(mozilla::dom::EventTarget* aOwner, mEventIsInternal = true; mEvent->time = PR_Now(); } + SetIsDOMBinding(); } nsDOMAnimationEvent::~nsDOMAnimationEvent() @@ -54,7 +55,7 @@ nsDOMAnimationEvent::GetAnimationName(nsAString & aAnimationName) NS_IMETHODIMP nsDOMAnimationEvent::GetElapsedTime(float *aElapsedTime) { - *aElapsedTime = AnimationEvent()->elapsedTime; + *aElapsedTime = ElapsedTime(); return NS_OK; } diff --git a/content/events/src/nsDOMAnimationEvent.h b/content/events/src/nsDOMAnimationEvent.h index 538ef206694..e7fe422f6a9 100644 --- a/content/events/src/nsDOMAnimationEvent.h +++ b/content/events/src/nsDOMAnimationEvent.h @@ -8,6 +8,7 @@ #include "nsDOMEvent.h" #include "nsIDOMAnimationEvent.h" #include "nsString.h" +#include "mozilla/dom/AnimationEventBinding.h" class nsAnimationEvent; @@ -24,6 +25,29 @@ public: NS_FORWARD_TO_NSDOMEVENT NS_DECL_NSIDOMANIMATIONEVENT + virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) + { + return mozilla::dom::AnimationEventBinding::Wrap(aCx, aScope, this); + } + + // xpidl implementation + // GetAnimationName(nsAString& aAnimationName); + + float ElapsedTime() + { + return AnimationEvent()->elapsedTime; + } + + void InitAnimationEvent(const nsAString& aType, + bool aCanBubble, + bool aCancelable, + const nsAString& aAnimationName, + float aElapsedTime, + mozilla::ErrorResult& aRv) + { + aRv = InitAnimationEvent(aType, aCanBubble, aCancelable, aAnimationName, + aElapsedTime); + } private: nsAnimationEvent* AnimationEvent() { NS_ABORT_IF_FALSE(mEvent->eventStructType == NS_ANIMATION_EVENT, diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 9a31b6d4c19..d3648af606c 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -78,6 +78,10 @@ DOMInterfaces = { +'AnimationEvent': { + 'nativeType': 'nsDOMAnimationEvent', +}, + 'ArchiveReader': { 'nativeType': 'mozilla::dom::file::ArchiveReader', }, diff --git a/dom/webidl/AnimationEvent.webidl b/dom/webidl/AnimationEvent.webidl new file mode 100644 index 00000000000..da07ac66cc3 --- /dev/null +++ b/dom/webidl/AnimationEvent.webidl @@ -0,0 +1,24 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * http://www.w3.org/TR/css3-animations/#animation-events- + * http://dev.w3.org/csswg/css3-animations/#animation-events- + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +interface AnimationEvent : Event { + readonly attribute DOMString animationName; + readonly attribute float elapsedTime; + + [Throws] + void initAnimationEvent(DOMString type, + boolean canBubble, + boolean cancelable, + DOMString animationName, + float elapsedTime); +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 6b2b69b5f95..96f96caacd6 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -9,6 +9,7 @@ generated_webidl_files = \ $(NULL) webidl_files = \ + AnimationEvent.webidl \ ArchiveReader.webidl \ AudioBuffer.webidl \ AudioBufferSourceNode.webidl \ From 0d003dc17809f3e8b81052f1297a57562ab4df71 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Thu, 14 Mar 2013 23:16:13 +0200 Subject: [PATCH 059/202] Bug 847599 - Paris binding for TransitionEvent, r=Ms2ger --HG-- extra : rebase_source : 0540a60c6a6d3ac1bef6df399e3feb7040f92a26 --- content/events/src/nsDOMTransitionEvent.cpp | 3 ++- content/events/src/nsDOMTransitionEvent.h | 24 +++++++++++++++++++++ dom/bindings/Bindings.conf | 4 ++++ dom/webidl/TransitionEvent.webidl | 24 +++++++++++++++++++++ dom/webidl/WebIDL.mk | 1 + 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 dom/webidl/TransitionEvent.webidl diff --git a/content/events/src/nsDOMTransitionEvent.cpp b/content/events/src/nsDOMTransitionEvent.cpp index 506bc9e2b0b..a9e883c0b6c 100644 --- a/content/events/src/nsDOMTransitionEvent.cpp +++ b/content/events/src/nsDOMTransitionEvent.cpp @@ -24,6 +24,7 @@ nsDOMTransitionEvent::nsDOMTransitionEvent(mozilla::dom::EventTarget* aOwner, mEventIsInternal = true; mEvent->time = PR_Now(); } + SetIsDOMBinding(); } nsDOMTransitionEvent::~nsDOMTransitionEvent() @@ -54,7 +55,7 @@ nsDOMTransitionEvent::GetPropertyName(nsAString & aPropertyName) NS_IMETHODIMP nsDOMTransitionEvent::GetElapsedTime(float *aElapsedTime) { - *aElapsedTime = TransitionEvent()->elapsedTime; + *aElapsedTime = ElapsedTime(); return NS_OK; } diff --git a/content/events/src/nsDOMTransitionEvent.h b/content/events/src/nsDOMTransitionEvent.h index e9f6a88b948..f96e80a9500 100644 --- a/content/events/src/nsDOMTransitionEvent.h +++ b/content/events/src/nsDOMTransitionEvent.h @@ -8,6 +8,7 @@ #include "nsDOMEvent.h" #include "nsIDOMTransitionEvent.h" #include "nsString.h" +#include "mozilla/dom/TransitionEventBinding.h" class nsTransitionEvent; @@ -24,6 +25,29 @@ public: NS_FORWARD_TO_NSDOMEVENT NS_DECL_NSIDOMTRANSITIONEVENT + virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) + { + return mozilla::dom::TransitionEventBinding::Wrap(aCx, aScope, this); + } + + // xpidl implementation + // GetPropertyName(nsAString& aPropertyName) + + float ElapsedTime() + { + return TransitionEvent()->elapsedTime; + } + + void InitTransitionEvent(const nsAString& aType, + bool aCanBubble, + bool aCancelable, + const nsAString& aPropertyName, + float aElapsedTime, + mozilla::ErrorResult& aRv) + { + aRv = InitTransitionEvent(aType, aCanBubble, aCancelable, aPropertyName, + aElapsedTime); + } private: nsTransitionEvent* TransitionEvent() { NS_ABORT_IF_FALSE(mEvent->eventStructType == NS_TRANSITION_EVENT, diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index d3648af606c..2c40250522e 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -875,6 +875,10 @@ DOMInterfaces = { 'wrapperCache': False }, +'TransitionEvent': { + 'nativeType': 'nsDOMTransitionEvent', +}, + 'TreeWalker': { 'wrapperCache': False, 'resultNotAddRefed': [ 'root', 'currentNode' ], diff --git a/dom/webidl/TransitionEvent.webidl b/dom/webidl/TransitionEvent.webidl new file mode 100644 index 00000000000..0614a9dcc0a --- /dev/null +++ b/dom/webidl/TransitionEvent.webidl @@ -0,0 +1,24 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * Transition events are defined in: + * http://www.w3.org/TR/css3-transitions/#transition-events- + * http://dev.w3.org/csswg/css3-transitions/#transition-events- + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +interface TransitionEvent : Event { + readonly attribute DOMString propertyName; + readonly attribute float elapsedTime; + + [Throws] + void initTransitionEvent(DOMString aType, + boolean aCanBubble, + boolean aCancelable, + DOMString aPropertyName, + float aElapsedTime); +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 96f96caacd6..f61a80f417e 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -231,6 +231,7 @@ webidl_files = \ TextDecoder.webidl \ TextEncoder.webidl \ TimeRanges.webidl \ + TransitionEvent.webidl \ TreeWalker.webidl \ UIEvent.webidl \ URL.webidl \ From 493383d8d5a45c666f22620deac3b58fb8650f2e Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Thu, 14 Mar 2013 23:18:20 +0200 Subject: [PATCH 060/202] Bug 847586 - Paris binding for MutationEvent, r=Ms2ger --HG-- extra : rebase_source : 7e20d191d214a1ac8eea2b6cb51718fafd5d0555 --- content/events/src/nsDOMMutationEvent.cpp | 15 +++------ content/events/src/nsDOMMutationEvent.h | 39 ++++++++++++++++++++++- dom/bindings/Bindings.conf | 3 ++ dom/webidl/MutationEvent.webidl | 33 +++++++++++++++++++ dom/webidl/WebIDL.mk | 1 + 5 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 dom/webidl/MutationEvent.webidl diff --git a/content/events/src/nsDOMMutationEvent.cpp b/content/events/src/nsDOMMutationEvent.cpp index e9ec97715d6..129a66da271 100644 --- a/content/events/src/nsDOMMutationEvent.cpp +++ b/content/events/src/nsDOMMutationEvent.cpp @@ -6,8 +6,6 @@ #include "nsCOMPtr.h" #include "nsDOMClassInfoID.h" #include "nsDOMMutationEvent.h" -#include "nsMutationEvent.h" - class nsPresContext; @@ -18,6 +16,7 @@ nsDOMMutationEvent::nsDOMMutationEvent(mozilla::dom::EventTarget* aOwner, aEvent ? aEvent : new nsMutationEvent(false, 0)) { mEventIsInternal = (aEvent == nullptr); + SetIsDOMBinding(); } nsDOMMutationEvent::~nsDOMMutationEvent() @@ -42,10 +41,9 @@ NS_IMPL_RELEASE_INHERITED(nsDOMMutationEvent, nsDOMEvent) NS_IMETHODIMP nsDOMMutationEvent::GetRelatedNode(nsIDOMNode** aRelatedNode) { - *aRelatedNode = nullptr; - nsMutationEvent* mutation = static_cast(mEvent); - *aRelatedNode = mutation->mRelatedNode; - NS_IF_ADDREF(*aRelatedNode); + nsCOMPtr relatedNode = GetRelatedNode(); + nsCOMPtr relatedDOMNode = relatedNode ? relatedNode->AsDOMNode() : nullptr; + relatedDOMNode.forget(aRelatedNode); return NS_OK; } @@ -79,10 +77,7 @@ nsDOMMutationEvent::GetAttrName(nsAString& aAttrName) NS_IMETHODIMP nsDOMMutationEvent::GetAttrChange(uint16_t* aAttrChange) { - *aAttrChange = 0; - nsMutationEvent* mutation = static_cast(mEvent); - if (mutation->mAttrChange) - *aAttrChange = mutation->mAttrChange; + *aAttrChange = AttrChange(); return NS_OK; } diff --git a/content/events/src/nsDOMMutationEvent.h b/content/events/src/nsDOMMutationEvent.h index 8d25f19e756..b7719a6f04a 100644 --- a/content/events/src/nsDOMMutationEvent.h +++ b/content/events/src/nsDOMMutationEvent.h @@ -8,6 +8,8 @@ #include "nsIDOMMutationEvent.h" #include "nsDOMEvent.h" +#include "nsMutationEvent.h" +#include "mozilla/dom/MutationEventBinding.h" class nsDOMMutationEvent : public nsDOMEvent, public nsIDOMMutationEvent @@ -17,13 +19,48 @@ public: nsPresContext* aPresContext, nsMutationEvent* aEvent); virtual ~nsDOMMutationEvent(); - + NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMMUTATIONEVENT // Forward to base class NS_FORWARD_TO_NSDOMEVENT + + virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) + { + return mozilla::dom::MutationEventBinding::Wrap(aCx, aScope, this); + } + + // xpidl implementation + // GetPrevValue(nsAString& aPrevValue); + // GetNewValue(nsAString& aNewValue); + // GetAttrName(nsAString& aAttrName); + + already_AddRefed GetRelatedNode() + { + nsCOMPtr n = + do_QueryInterface(static_cast(mEvent)->mRelatedNode); + return n.forget(); + } + + uint16_t AttrChange() + { + return static_cast(mEvent)->mAttrChange; + } + + void InitMutationEvent(const nsAString& aType, + bool& aCanBubble, bool& aCancelable, + nsINode* aRelatedNode, + const nsAString& aPrevValue, + const nsAString& aNewValue, + const nsAString& aAttrName, + uint16_t& aAttrChange, mozilla::ErrorResult& aRv) + { + aRv = InitMutationEvent(aType, aCanBubble, aCancelable, + aRelatedNode ? aRelatedNode->AsDOMNode() : nullptr, + aPrevValue, aNewValue, aAttrName, aAttrChange); + } }; #endif // nsDOMMutationEvent_h__ diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 2c40250522e..7fa95630ba9 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -518,6 +518,9 @@ DOMInterfaces = { 'workers': True, }], +'MutationEvent': { + 'nativeType': 'nsDOMMutationEvent', +}, 'MutationObserver': { 'nativeType': 'nsDOMMutationObserver', diff --git a/dom/webidl/MutationEvent.webidl b/dom/webidl/MutationEvent.webidl new file mode 100644 index 00000000000..43c7b1cd06c --- /dev/null +++ b/dom/webidl/MutationEvent.webidl @@ -0,0 +1,33 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ +interface MutationEvent : Event +{ + const unsigned short MODIFICATION = 1; + const unsigned short ADDITION = 2; + const unsigned short REMOVAL = 3; + + readonly attribute Node? relatedNode; + readonly attribute DOMString prevValue; + readonly attribute DOMString newValue; + readonly attribute DOMString attrName; + readonly attribute unsigned short attrChange; + + [Throws] + void initMutationEvent(DOMString type, + boolean canBubble, + boolean cancelable, + Node? relatedNode, + DOMString prevValue, + DOMString newValue, + DOMString attrName, + unsigned short attrChange); +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index f61a80f417e..38d3e898c58 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -126,6 +126,7 @@ webidl_files = \ Location.webidl \ MediaStream.webidl \ MessageEvent.webidl \ + MutationEvent.webidl \ MutationObserver.webidl \ Node.webidl \ NodeFilter.webidl \ From 5f0ca962bf24c0c71625a4b8f4616be079a959f0 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Fri, 15 Mar 2013 11:00:20 +0200 Subject: [PATCH 061/202] bug 847599, trying clobbering, r=me --HG-- extra : rebase_source : eca1b3730b4fbeec021b46546c622941c35f39ed --- CLOBBER | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLOBBER b/CLOBBER index 0f4a591320f..5b7885b8efe 100644 --- a/CLOBBER +++ b/CLOBBER @@ -15,4 +15,4 @@ # # Note: The description below will be part of the error message shown to users. # -Bug 850713 requires a clobber +Bug 847599 requires a clobber From e643cf022c87cb28cc7782235e7a23fd53777176 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 15 Mar 2013 00:44:00 +0000 Subject: [PATCH 062/202] Bug 838652 - Use res-auto namespace alias instead of preprocessing files (r=mfinkle) --HG-- rename : mobile/android/base/resources/color/abouthome_section_more_text.xml.in => mobile/android/base/resources/color/abouthome_section_more_text.xml rename : mobile/android/base/resources/color/abouthome_section_subtitle.xml.in => mobile/android/base/resources/color/abouthome_section_subtitle.xml rename : mobile/android/base/resources/color/abouthome_section_title.xml.in => mobile/android/base/resources/color/abouthome_section_title.xml rename : mobile/android/base/resources/color/awesome_bar_title.xml.in => mobile/android/base/resources/color/awesome_bar_title.xml rename : mobile/android/base/resources/color/awesome_bar_title_hint.xml.in => mobile/android/base/resources/color/awesome_bar_title_hint.xml rename : mobile/android/base/resources/drawable/abouthome_logo.xml.in => mobile/android/base/resources/drawable/abouthome_logo.xml rename : mobile/android/base/resources/drawable/address_bar_bg.xml.in => mobile/android/base/resources/drawable/address_bar_bg.xml rename : mobile/android/base/resources/drawable/address_bar_nav_button.xml.in => mobile/android/base/resources/drawable/address_bar_nav_button.xml rename : mobile/android/base/resources/drawable/address_bar_url.xml.in => mobile/android/base/resources/drawable/address_bar_url.xml rename : mobile/android/base/resources/drawable/menu_item_state.xml.in => mobile/android/base/resources/drawable/menu_item_state.xml rename : mobile/android/base/resources/drawable/menu_level.xml.in => mobile/android/base/resources/drawable/menu_level.xml rename : mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in => mobile/android/base/resources/layout-land-v14/browser_toolbar.xml rename : mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in => mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml rename : mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml.in => mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml rename : mobile/android/base/resources/layout-large-v11/awesomebar_search.xml.in => mobile/android/base/resources/layout-large-v11/awesomebar_search.xml rename : mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in => mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml rename : mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in => mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml rename : mobile/android/base/resources/layout-xlarge-v11/awesomebar_search.xml.in => mobile/android/base/resources/layout-xlarge-v11/awesomebar_search.xml rename : mobile/android/base/resources/layout/abouthome_content.xml.in => mobile/android/base/resources/layout/abouthome_content.xml rename : mobile/android/base/resources/layout/awesomebar_search.xml.in => mobile/android/base/resources/layout/awesomebar_search.xml rename : mobile/android/base/resources/layout/awesomebar_suggestion_row.xml.in => mobile/android/base/resources/layout/awesomebar_suggestion_row.xml rename : mobile/android/base/resources/layout/awesomebar_tab_indicator.xml.in => mobile/android/base/resources/layout/awesomebar_tab_indicator.xml rename : mobile/android/base/resources/layout/browser_toolbar.xml.in => mobile/android/base/resources/layout/browser_toolbar.xml rename : mobile/android/base/resources/layout/browser_toolbar_menu.xml.in => mobile/android/base/resources/layout/browser_toolbar_menu.xml rename : mobile/android/base/resources/layout/gecko_app.xml.in => mobile/android/base/resources/layout/gecko_app.xml rename : mobile/android/base/resources/layout/tabs_panel.xml.in => mobile/android/base/resources/layout/tabs_panel.xml rename : mobile/android/base/resources/layout/text_selection_handles.xml.in => mobile/android/base/resources/layout/text_selection_handles.xml rename : mobile/android/base/resources/menu-large-v11/browser_app_menu.xml.in => mobile/android/base/resources/menu-large-v11/browser_app_menu.xml rename : mobile/android/base/resources/menu-v11/browser_app_menu.xml.in => mobile/android/base/resources/menu-v11/browser_app_menu.xml rename : mobile/android/base/resources/menu-xlarge-v11/browser_app_menu.xml.in => mobile/android/base/resources/menu-xlarge-v11/browser_app_menu.xml rename : mobile/android/base/resources/menu/browser_app_menu.xml.in => mobile/android/base/resources/menu/browser_app_menu.xml --- CLOBBER | 2 +- mobile/android/base/Makefile.in | 82 +++++++++++-------- ...xml.in => abouthome_section_more_text.xml} | 3 +- ....xml.in => abouthome_section_subtitle.xml} | 3 +- ...tle.xml.in => abouthome_section_title.xml} | 3 +- ...bar_title.xml.in => awesome_bar_title.xml} | 3 +- ...hint.xml.in => awesome_bar_title_hint.xml} | 3 +- ...outhome_logo.xml.in => abouthome_logo.xml} | 3 +- ...dress_bar_bg.xml.in => address_bar_bg.xml} | 3 +- ...tton.xml.in => address_bar_nav_button.xml} | 3 +- ...ess_bar_url.xml.in => address_bar_url.xml} | 3 +- ..._item_state.xml.in => menu_item_state.xml} | 3 +- .../{menu_level.xml.in => menu_level.xml} | 3 +- ...ser_toolbar.xml.in => browser_toolbar.xml} | 3 +- ...r_menu.xml.in => browser_toolbar_menu.xml} | 3 +- .../{tabs_panel.xml.in => tabs_panel.xml} | 3 +- ...ar_search.xml.in => awesomebar_search.xml} | 3 +- ...r_menu.xml.in => browser_toolbar_menu.xml} | 3 +- ...e_content.xml.in => abouthome_content.xml} | 3 +- ...ar_search.xml.in => awesomebar_search.xml} | 3 +- ...e_content.xml.in => abouthome_content.xml} | 3 +- ...ar_search.xml.in => awesomebar_search.xml} | 3 +- ...w.xml.in => awesomebar_suggestion_row.xml} | 2 - ...or.xml.in => awesomebar_tab_indicator.xml} | 3 +- ...ser_toolbar.xml.in => browser_toolbar.xml} | 3 +- ...r_menu.xml.in => browser_toolbar_menu.xml} | 3 +- .../{gecko_app.xml.in => gecko_app.xml} | 1 - .../{tabs_panel.xml.in => tabs_panel.xml} | 3 +- ...dles.xml.in => text_selection_handles.xml} | 3 +- ...r_app_menu.xml.in => browser_app_menu.xml} | 0 ...r_app_menu.xml.in => browser_app_menu.xml} | 0 ...r_app_menu.xml.in => browser_app_menu.xml} | 0 ...r_app_menu.xml.in => browser_app_menu.xml} | 0 .../base/resources/xml/preferences.xml.in | 2 +- 34 files changed, 75 insertions(+), 89 deletions(-) rename mobile/android/base/resources/color/{abouthome_section_more_text.xml.in => abouthome_section_more_text.xml} (84%) rename mobile/android/base/resources/color/{abouthome_section_subtitle.xml.in => abouthome_section_subtitle.xml} (84%) rename mobile/android/base/resources/color/{abouthome_section_title.xml.in => abouthome_section_title.xml} (84%) rename mobile/android/base/resources/color/{awesome_bar_title.xml.in => awesome_bar_title.xml} (86%) rename mobile/android/base/resources/color/{awesome_bar_title_hint.xml.in => awesome_bar_title_hint.xml} (82%) rename mobile/android/base/resources/drawable/{abouthome_logo.xml.in => abouthome_logo.xml} (86%) rename mobile/android/base/resources/drawable/{address_bar_bg.xml.in => address_bar_bg.xml} (83%) rename mobile/android/base/resources/drawable/{address_bar_nav_button.xml.in => address_bar_nav_button.xml} (86%) rename mobile/android/base/resources/drawable/{address_bar_url.xml.in => address_bar_url.xml} (92%) rename mobile/android/base/resources/drawable/{menu_item_state.xml.in => menu_item_state.xml} (88%) rename mobile/android/base/resources/drawable/{menu_level.xml.in => menu_level.xml} (85%) rename mobile/android/base/resources/layout-land-v14/{browser_toolbar.xml.in => browser_toolbar.xml} (98%) rename mobile/android/base/resources/layout-land-v14/{browser_toolbar_menu.xml.in => browser_toolbar_menu.xml} (98%) rename mobile/android/base/resources/layout-large-land-v11/{tabs_panel.xml.in => tabs_panel.xml} (97%) rename mobile/android/base/resources/layout-large-v11/{awesomebar_search.xml.in => awesomebar_search.xml} (96%) rename mobile/android/base/resources/layout-large-v11/{browser_toolbar_menu.xml.in => browser_toolbar_menu.xml} (98%) rename mobile/android/base/resources/layout-xlarge-land-v11/{abouthome_content.xml.in => abouthome_content.xml} (98%) rename mobile/android/base/resources/layout-xlarge-v11/{awesomebar_search.xml.in => awesomebar_search.xml} (96%) rename mobile/android/base/resources/layout/{abouthome_content.xml.in => abouthome_content.xml} (98%) rename mobile/android/base/resources/layout/{awesomebar_search.xml.in => awesomebar_search.xml} (96%) rename mobile/android/base/resources/layout/{awesomebar_suggestion_row.xml.in => awesomebar_suggestion_row.xml} (93%) rename mobile/android/base/resources/layout/{awesomebar_tab_indicator.xml.in => awesomebar_tab_indicator.xml} (88%) rename mobile/android/base/resources/layout/{browser_toolbar.xml.in => browser_toolbar.xml} (98%) rename mobile/android/base/resources/layout/{browser_toolbar_menu.xml.in => browser_toolbar_menu.xml} (98%) rename mobile/android/base/resources/layout/{gecko_app.xml.in => gecko_app.xml} (99%) rename mobile/android/base/resources/layout/{tabs_panel.xml.in => tabs_panel.xml} (96%) rename mobile/android/base/resources/layout/{text_selection_handles.xml.in => text_selection_handles.xml} (94%) rename mobile/android/base/resources/menu-large-v11/{browser_app_menu.xml.in => browser_app_menu.xml} (100%) rename mobile/android/base/resources/menu-v11/{browser_app_menu.xml.in => browser_app_menu.xml} (100%) rename mobile/android/base/resources/menu-xlarge-v11/{browser_app_menu.xml.in => browser_app_menu.xml} (100%) rename mobile/android/base/resources/menu/{browser_app_menu.xml.in => browser_app_menu.xml} (100%) diff --git a/CLOBBER b/CLOBBER index 5b7885b8efe..08e2783b444 100644 --- a/CLOBBER +++ b/CLOBBER @@ -15,4 +15,4 @@ # # Note: The description below will be part of the error message shown to users. # -Bug 847599 requires a clobber +Bug 838652 requires a clobber diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 6ba3f1282ca..ece7ef79ddb 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -249,42 +249,10 @@ FENNEC_PP_JAVA_FILES = \ $(NULL) FENNEC_PP_XML_FILES = \ - res/color/abouthome_section_more_text.xml \ - res/color/abouthome_section_subtitle.xml \ - res/color/abouthome_section_title.xml \ - res/color/awesome_bar_title.xml \ - res/color/awesome_bar_title_hint.xml \ - res/drawable/abouthome_logo.xml \ - res/drawable/address_bar_bg.xml \ - res/drawable/address_bar_nav_button.xml \ - res/drawable/address_bar_url.xml \ - res/drawable/menu_item_state.xml \ - res/drawable/menu_level.xml \ - res/layout/abouthome_content.xml \ - res/layout/awesomebar_search.xml \ - res/layout/awesomebar_suggestion_row.xml \ - res/layout/awesomebar_tab_indicator.xml \ - res/layout/browser_toolbar.xml \ - res/layout/browser_toolbar_menu.xml \ - res/layout-land-v14/browser_toolbar.xml \ - res/layout-land-v14/browser_toolbar_menu.xml \ - res/layout-large-v11/awesomebar_search.xml \ - res/layout-large-v11/browser_toolbar_menu.xml \ - res/layout-large-land-v11/tabs_panel.xml \ - res/layout/gecko_app.xml \ - res/layout/tabs_panel.xml \ - res/layout/text_selection_handles.xml \ - res/layout-xlarge-land-v11/abouthome_content.xml \ - res/layout-xlarge-v11/awesomebar_search.xml \ res/xml/preferences.xml \ res/xml/searchable.xml \ - res/menu/browser_app_menu.xml \ - res/menu-v11/browser_app_menu.xml \ - res/menu-large-v11/browser_app_menu.xml \ - res/menu-xlarge-v11/browser_app_menu.xml \ $(NULL) - ifneq (,$(findstring -march=armv7,$(OS_CFLAGS))) MIN_CPU_VERSION=7 else @@ -397,6 +365,7 @@ endif RES_LAYOUT = \ $(SYNC_RES_LAYOUT) \ + res/layout/abouthome_content.xml \ res/layout/autocomplete_list.xml \ res/layout/autocomplete_list_item.xml \ res/layout/awesomebar.xml \ @@ -407,16 +376,22 @@ RES_LAYOUT = \ res/layout/awesomebar_allpages_list.xml \ res/layout/awesomebar_list.xml \ res/layout/awesomebar_row.xml \ + res/layout/awesomebar_search.xml \ + res/layout/awesomebar_suggestion_row.xml \ res/layout/awesomebar_suggestion_item.xml \ res/layout/awesomebar_suggestion_prompt.xml \ + res/layout/awesomebar_tab_indicator.xml \ res/layout/awesomebar_tabs.xml \ res/layout/bookmark_edit.xml \ + res/layout/browser_toolbar.xml \ + res/layout/browser_toolbar_menu.xml \ res/layout/datetime_picker.xml \ res/layout/doorhangerpopup.xml \ res/layout/doorhanger.xml \ res/layout/doorhanger_button.xml \ res/layout/find_in_page_content.xml \ res/layout/font_size_preference.xml \ + res/layout/gecko_app.xml \ res/layout/gecko_appwidget.xml \ res/layout/web_app.xml \ res/layout/launch_app_list.xml \ @@ -434,11 +409,13 @@ RES_LAYOUT = \ res/layout/site_identity_popup.xml \ res/layout/remote_tabs_child.xml \ res/layout/remote_tabs_group.xml \ + res/layout/tabs_panel.xml \ res/layout/tabs_counter.xml \ res/layout/tabs_panel_header.xml \ res/layout/tabs_panel_indicator.xml \ res/layout/tabs_item_cell.xml \ res/layout/tabs_item_row.xml \ + res/layout/text_selection_handles.xml \ res/layout/list_item_header.xml \ res/layout/select_dialog_list.xml \ res/layout/select_dialog_multichoice.xml \ @@ -450,22 +427,35 @@ RES_LAYOUT = \ res/layout/validation_message.xml \ $(NULL) +RES_LAYOUT_LAND_V14 = \ + res/layout-land-v14/browser_toolbar.xml \ + res/layout-land-v14/browser_toolbar_menu.xml \ + $(NULL) + RES_LAYOUT_LARGE_V11 = \ + res/layout-large-v11/awesomebar_search.xml \ + res/layout-large-v11/browser_toolbar_menu.xml \ res/layout-large-v11/doorhangerpopup.xml \ res/layout-large-v11/site_identity_popup.xml \ $(NULL) RES_LAYOUT_LARGE_LAND_V11 = \ + res/layout-large-land-v11/tabs_panel.xml \ res/layout-large-land-v11/tabs_panel_header.xml \ res/layout-large-land-v11/tabs_panel_footer.xml \ $(NULL) RES_LAYOUT_XLARGE_V11 = \ + res/layout-xlarge-v11/awesomebar_search.xml \ res/layout-xlarge-v11/font_size_preference.xml \ res/layout-xlarge-v11/remote_tabs_child.xml \ res/layout-xlarge-v11/remote_tabs_group.xml \ $(NULL) +RES_LAYOUT_XLARGE_LAND_V11 = \ + res/layout-xlarge-land-v11/abouthome_content.xml \ + $(NULL) + RES_VALUES = \ $(SYNC_RES_VALUES) \ res/values/attrs.xml \ @@ -518,7 +508,7 @@ RES_VALUES_V14 = \ $(NULL) RES_XML = \ - res/xml/gecko_appwidget_info.xml \ + res/xml/gecko_appwidget_info.xml \ $(SYNC_RES_XML) \ $(NULL) @@ -970,17 +960,35 @@ RES_DRAWABLE_XLARGE_XHDPI_V11 = \ $(NULL) RES_COLOR = \ + res/color/abouthome_section_more_text.xml \ + res/color/abouthome_section_subtitle.xml \ + res/color/abouthome_section_title.xml \ + res/color/awesome_bar_title.xml \ + res/color/awesome_bar_title_hint.xml \ res/color/menu_item_title.xml \ res/color/select_item_multichoice.xml \ $(NULL) RES_MENU = \ res/menu/awesomebar_contextmenu.xml \ + res/menu/browser_app_menu.xml \ res/menu/gecko_app_menu.xml \ res/menu/titlebar_contextmenu.xml \ res/menu/abouthome_topsites_contextmenu.xml \ $(NULL) +RES_MENU_V11 = \ + res/menu-v11/browser_app_menu.xml \ + $(NULL) + +RES_MENU_LARGE_V11 = \ + res/menu-large-v11/browser_app_menu.xml \ + $(NULL) + +RES_MENU_XLARGE_V11 = \ + res/menu-xlarge-v11/browser_app_menu.xml \ + $(NULL) + JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar ifdef MOZ_CRASHREPORTER @@ -991,10 +999,14 @@ endif MOZ_ANDROID_DRAWABLES += \ $(SYNC_RES_DRAWABLE) \ + mobile/android/base/resources/drawable/abouthome_logo.xml \ mobile/android/base/resources/drawable/abouthome_promo_box.xml \ mobile/android/base/resources/drawable/action_bar_button.xml \ mobile/android/base/resources/drawable/action_bar_button_inverse.xml \ + mobile/android/base/resources/drawable/address_bar_bg.xml \ mobile/android/base/resources/drawable/address_bar_bg_shadow_repeat.xml \ + mobile/android/base/resources/drawable/address_bar_nav_button.xml \ + mobile/android/base/resources/drawable/address_bar_url.xml \ mobile/android/base/resources/drawable/awesomebar_row_favicon_bg.xml \ mobile/android/base/resources/drawable/awesomebar_tab_indicator.xml \ mobile/android/base/resources/drawable/awesomebar_tab_selected.xml \ @@ -1006,6 +1018,8 @@ MOZ_ANDROID_DRAWABLES += \ mobile/android/base/resources/drawable/ic_menu_desktop_mode_on.xml \ mobile/android/base/resources/drawable/ic_menu_quit.xml \ mobile/android/base/resources/drawable/menu_button.xml \ + mobile/android/base/resources/drawable/menu_item_state.xml \ + mobile/android/base/resources/drawable/menu_level.xml \ mobile/android/base/resources/drawable/progress_spinner.xml \ mobile/android/base/resources/drawable/remote_tabs_child_divider.xml \ mobile/android/base/resources/drawable/site_security_level.xml \ @@ -1024,7 +1038,7 @@ MOZ_BRANDING_DRAWABLE_MDPI = $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRE MOZ_BRANDING_DRAWABLE_HDPI = $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-hdpi.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-hdpi.mn | tr '\n' ' '; fi) MOZ_BRANDING_DRAWABLE_XHDPI = $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-xhdpi.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-xhdpi.mn | tr '\n' ' '; fi) -RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_LARGE_LAND_V11) $(RES_LAYOUT_LARGE_V11) $(RES_LAYOUT_XLARGE_V11) $(RES_VALUES) $(RES_VALUES_LAND) $(RES_VALUES_V11) $(RES_VALUES_LARGE_V11) $(RES_VALUES_LARGE_LAND_V11) $(RES_VALUES_XLARGE_V11) $(RES_VALUES_LAND_V14) $(RES_VALUES_V14) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_MDPI) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_XHDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_DRAWABLE_LARGE_LAND_V11) $(RES_DRAWABLE_LARGE_MDPI_V11) $(RES_DRAWABLE_LARGE_HDPI_V11) $(RES_DRAWABLE_LARGE_XHDPI_V11) $(RES_DRAWABLE_XLARGE_MDPI_V11) $(RES_DRAWABLE_XLARGE_HDPI_V11) $(RES_DRAWABLE_XLARGE_XHDPI_V11) $(RES_COLOR) $(RES_MENU) +RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_LAND_V14) $(RES_LAYOUT_LARGE_LAND_V11) $(RES_LAYOUT_LARGE_V11) $(RES_LAYOUT_XLARGE_V11) $(RES_LAYOUT_XLARGE_LAND_V11) $(RES_VALUES) $(RES_VALUES_LAND) $(RES_VALUES_V11) $(RES_VALUES_LARGE_V11) $(RES_VALUES_LARGE_LAND_V11) $(RES_VALUES_XLARGE_V11) $(RES_VALUES_LAND_V14) $(RES_VALUES_V14) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_MDPI) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_XHDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_DRAWABLE_LARGE_LAND_V11) $(RES_DRAWABLE_LARGE_MDPI_V11) $(RES_DRAWABLE_LARGE_HDPI_V11) $(RES_DRAWABLE_LARGE_XHDPI_V11) $(RES_DRAWABLE_XLARGE_MDPI_V11) $(RES_DRAWABLE_XLARGE_HDPI_V11) $(RES_DRAWABLE_XLARGE_XHDPI_V11) $(RES_COLOR) $(RES_MENU) $(RES_MENU_V11) $(RES_MENU_LARGE_V11) $(RES_MENU_XLARGE_V11) RES_DIRS= \ res/layout \ diff --git a/mobile/android/base/resources/color/abouthome_section_more_text.xml.in b/mobile/android/base/resources/color/abouthome_section_more_text.xml similarity index 84% rename from mobile/android/base/resources/color/abouthome_section_more_text.xml.in rename to mobile/android/base/resources/color/abouthome_section_more_text.xml index 3c636a48fce..b3ac4d7a8b6 100644 --- a/mobile/android/base/resources/color/abouthome_section_more_text.xml.in +++ b/mobile/android/base/resources/color/abouthome_section_more_text.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/color/abouthome_section_subtitle.xml.in b/mobile/android/base/resources/color/abouthome_section_subtitle.xml similarity index 84% rename from mobile/android/base/resources/color/abouthome_section_subtitle.xml.in rename to mobile/android/base/resources/color/abouthome_section_subtitle.xml index abaef7b02da..dbf7e480d6f 100644 --- a/mobile/android/base/resources/color/abouthome_section_subtitle.xml.in +++ b/mobile/android/base/resources/color/abouthome_section_subtitle.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/color/abouthome_section_title.xml.in b/mobile/android/base/resources/color/abouthome_section_title.xml similarity index 84% rename from mobile/android/base/resources/color/abouthome_section_title.xml.in rename to mobile/android/base/resources/color/abouthome_section_title.xml index f5bfc7cc4c9..47081f3ce89 100644 --- a/mobile/android/base/resources/color/abouthome_section_title.xml.in +++ b/mobile/android/base/resources/color/abouthome_section_title.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/color/awesome_bar_title.xml.in b/mobile/android/base/resources/color/awesome_bar_title.xml similarity index 86% rename from mobile/android/base/resources/color/awesome_bar_title.xml.in rename to mobile/android/base/resources/color/awesome_bar_title.xml index f84b70cc7cc..789328c6acc 100644 --- a/mobile/android/base/resources/color/awesome_bar_title.xml.in +++ b/mobile/android/base/resources/color/awesome_bar_title.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/color/awesome_bar_title_hint.xml.in b/mobile/android/base/resources/color/awesome_bar_title_hint.xml similarity index 82% rename from mobile/android/base/resources/color/awesome_bar_title_hint.xml.in rename to mobile/android/base/resources/color/awesome_bar_title_hint.xml index 5ca3a2b6fca..0c748ac0bf8 100644 --- a/mobile/android/base/resources/color/awesome_bar_title_hint.xml.in +++ b/mobile/android/base/resources/color/awesome_bar_title_hint.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/drawable/abouthome_logo.xml.in b/mobile/android/base/resources/drawable/abouthome_logo.xml similarity index 86% rename from mobile/android/base/resources/drawable/abouthome_logo.xml.in rename to mobile/android/base/resources/drawable/abouthome_logo.xml index fe919b868a5..1acd0bf6cfa 100644 --- a/mobile/android/base/resources/drawable/abouthome_logo.xml.in +++ b/mobile/android/base/resources/drawable/abouthome_logo.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/drawable/address_bar_bg.xml.in b/mobile/android/base/resources/drawable/address_bar_bg.xml similarity index 83% rename from mobile/android/base/resources/drawable/address_bar_bg.xml.in rename to mobile/android/base/resources/drawable/address_bar_bg.xml index 9fad452417d..05e49f13944 100644 --- a/mobile/android/base/resources/drawable/address_bar_bg.xml.in +++ b/mobile/android/base/resources/drawable/address_bar_bg.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/drawable/address_bar_nav_button.xml.in b/mobile/android/base/resources/drawable/address_bar_nav_button.xml similarity index 86% rename from mobile/android/base/resources/drawable/address_bar_nav_button.xml.in rename to mobile/android/base/resources/drawable/address_bar_nav_button.xml index 5f0b7c60e1d..71f164e2a1b 100644 --- a/mobile/android/base/resources/drawable/address_bar_nav_button.xml.in +++ b/mobile/android/base/resources/drawable/address_bar_nav_button.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/drawable/address_bar_url.xml.in b/mobile/android/base/resources/drawable/address_bar_url.xml similarity index 92% rename from mobile/android/base/resources/drawable/address_bar_url.xml.in rename to mobile/android/base/resources/drawable/address_bar_url.xml index dce77359bbf..c8b9da2b3f8 100644 --- a/mobile/android/base/resources/drawable/address_bar_url.xml.in +++ b/mobile/android/base/resources/drawable/address_bar_url.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/drawable/menu_level.xml.in b/mobile/android/base/resources/drawable/menu_level.xml similarity index 85% rename from mobile/android/base/resources/drawable/menu_level.xml.in rename to mobile/android/base/resources/drawable/menu_level.xml index 15e2c75c607..88617f4b761 100644 --- a/mobile/android/base/resources/drawable/menu_level.xml.in +++ b/mobile/android/base/resources/drawable/menu_level.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml similarity index 98% rename from mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in rename to mobile/android/base/resources/layout-land-v14/browser_toolbar.xml index c0fab6ceb35..ca5570deaf7 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml @@ -1,11 +1,10 @@ -#filter substitution diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml similarity index 98% rename from mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in rename to mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml index 0780eaada1d..80c2f22862f 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml @@ -1,11 +1,10 @@ -#filter substitution diff --git a/mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml.in b/mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml similarity index 97% rename from mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml.in rename to mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml index 56159e2275a..47233b02137 100644 --- a/mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml.in +++ b/mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in b/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml similarity index 98% rename from mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in rename to mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml index b35214b6912..bf1b4b3ea33 100644 --- a/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in +++ b/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml @@ -1,11 +1,10 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> + xmlns:gecko="http://schemas.android.com/apk/res-auto"> diff --git a/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout/browser_toolbar_menu.xml similarity index 98% rename from mobile/android/base/resources/layout/browser_toolbar_menu.xml.in rename to mobile/android/base/resources/layout/browser_toolbar_menu.xml index 7699e5ab6b3..77235f76ce7 100644 --- a/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout/browser_toolbar_menu.xml @@ -1,11 +1,10 @@ -#filter substitution diff --git a/mobile/android/base/resources/layout/gecko_app.xml.in b/mobile/android/base/resources/layout/gecko_app.xml similarity index 99% rename from mobile/android/base/resources/layout/gecko_app.xml.in rename to mobile/android/base/resources/layout/gecko_app.xml index f7ea076cee5..4ba87552842 100644 --- a/mobile/android/base/resources/layout/gecko_app.xml.in +++ b/mobile/android/base/resources/layout/gecko_app.xml @@ -1,4 +1,3 @@ -#filter substitution + xmlns:gecko="http://schemas.android.com/apk/res-auto"> + xmlns:gecko="http://schemas.android.com/apk/res-auto"> From 97473daf3133d6fc76350e80bca02ced0ec348c0 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 15 Mar 2013 09:48:12 +0000 Subject: [PATCH 063/202] Bug 850602 - Fix handling of null AdapterDataSetObserver in TwoWayView (r=mfinkle) --- mobile/android/base/widget/TwoWayView.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/widget/TwoWayView.java b/mobile/android/base/widget/TwoWayView.java index deefeca0397..ea9f11c5a87 100644 --- a/mobile/android/base/widget/TwoWayView.java +++ b/mobile/android/base/widget/TwoWayView.java @@ -335,7 +335,7 @@ public class TwoWayView extends AdapterView implements mCheckStates = null; mRecycler = new RecycleBin(); - mDataSetObserver = new AdapterDataSetObserver(); + mDataSetObserver = null; mAreAllItemsSelectable = true; @@ -723,7 +723,7 @@ public class TwoWayView extends AdapterView implements @Override public void setAdapter(ListAdapter adapter) { - if (mAdapter != null) { + if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } @@ -748,7 +748,9 @@ public class TwoWayView extends AdapterView implements mOldItemCount = mItemCount; mItemCount = adapter.getCount(); + mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); + mRecycler.setViewTypeCount(adapter.getViewTypeCount()); mHasStableIds = adapter.hasStableIds(); From 626c4a4a592c2b8fbc7e5b756c7f06540519bee9 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 15 Mar 2013 09:48:12 +0000 Subject: [PATCH 064/202] Bug 850602 - Handle non-recyclable scrap views properly in TwoWayView (r=mfinkle) --- mobile/android/base/widget/TwoWayView.java | 24 ++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/widget/TwoWayView.java b/mobile/android/base/widget/TwoWayView.java index ea9f11c5a87..ed72045dec9 100644 --- a/mobile/android/base/widget/TwoWayView.java +++ b/mobile/android/base/widget/TwoWayView.java @@ -4374,6 +4374,10 @@ public class TwoWayView extends AdapterView implements } } + public boolean shouldRecycleViewType(int viewType) { + return viewType >= 0; + } + void clear() { if (mViewTypeCount == 1) { final ArrayList scrap = mCurrentScrap; @@ -4473,10 +4477,26 @@ public class TwoWayView extends AdapterView implements lp.scrappedFromPosition = position; + final int viewType = lp.viewType; + final boolean scrapHasTransientState = ViewCompat.hasTransientState(scrap); + + // Don't put views that should be ignored into the scrap heap + if (!shouldRecycleViewType(viewType) || scrapHasTransientState) { + if (scrapHasTransientState) { + if (mTransientStateViews == null) { + mTransientStateViews = new SparseArrayCompat(); + } + + mTransientStateViews.put(position, scrap); + } + + return; + } + if (mViewTypeCount == 1) { mCurrentScrap.add(scrap); } else { - mScrapViews[lp.viewType].add(scrap); + mScrapViews[viewType].add(scrap); } if (mRecyclerListener != null) { @@ -4500,7 +4520,7 @@ public class TwoWayView extends AdapterView implements activeViews[i] = null; final boolean scrapHasTransientState = ViewCompat.hasTransientState(victim); - if (scrapHasTransientState) { + if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) { if (scrapHasTransientState) { removeDetachedView(victim, false); From f60aa7f56116fe3fb1832e022b89c003cd916fc7 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 15 Mar 2013 09:48:12 +0000 Subject: [PATCH 065/202] Bug 850602 - Update empty view state when dataset changes (r=mfinkle) --- mobile/android/base/widget/TwoWayView.java | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/widget/TwoWayView.java b/mobile/android/base/widget/TwoWayView.java index ed72045dec9..9823aeb810c 100644 --- a/mobile/android/base/widget/TwoWayView.java +++ b/mobile/android/base/widget/TwoWayView.java @@ -213,6 +213,8 @@ public class TwoWayView extends AdapterView implements private OnScrollListener mOnScrollListener; private int mLastScrollState; + private View mEmptyView; + public interface OnScrollListener { /** @@ -776,6 +778,7 @@ public class TwoWayView extends AdapterView implements checkSelectionChanged(); } + updateEmptyStatus(); requestLayout(); } @@ -4612,6 +4615,38 @@ public class TwoWayView extends AdapterView implements } } + @Override + public void setEmptyView(View emptyView) { + super.setEmptyView(emptyView); + mEmptyView = emptyView; + updateEmptyStatus(); + } + + private void updateEmptyStatus() { + boolean isEmpty = mAdapter == null || mAdapter.isEmpty(); + if (isEmpty) { + if (mEmptyView != null) { + mEmptyView.setVisibility(View.VISIBLE); + setVisibility(View.GONE); + } else { + // If the caller just removed our empty view, make sure the list + // view is visible + setVisibility(View.VISIBLE); + } + + // We are now GONE, so pending layouts will not be dispatched. + // Force one here to make sure that the state of the list matches + // the state of the adapter. + if (mDataChanged) { + this.onLayout(false, getLeft(), getTop(), getRight(), getBottom()); + } + } else { + if (mEmptyView != null) + mEmptyView.setVisibility(View.GONE); + setVisibility(View.VISIBLE); + } + } + private class AdapterDataSetObserver extends DataSetObserver { private Parcelable mInstanceState = null; @@ -4630,7 +4665,7 @@ public class TwoWayView extends AdapterView implements } else { rememberSyncState(); } - + updateEmptyStatus(); requestLayout(); } @@ -4656,6 +4691,7 @@ public class TwoWayView extends AdapterView implements mNeedSync = false; + updateEmptyStatus(); requestLayout(); } } From a5c75c91bce9b66448656d12c1ba156256ae0981 Mon Sep 17 00:00:00 2001 From: Aryeh Gregor Date: Fri, 15 Mar 2013 12:21:52 +0200 Subject: [PATCH 066/202] Bug 801562 - Remove Node.isSupported; r=bz --HG-- rename : content/svg/content/test/test_isSupported.xhtml => content/svg/content/test/test_hasFeature.xhtml --- content/base/public/nsINode.h | 6 - content/base/src/nsINode.cpp | 6 - content/svg/content/src/nsSVGElement.cpp | 10 -- content/svg/content/src/nsSVGElement.h | 4 - content/svg/content/test/Makefile.in | 2 +- ...sSupported.xhtml => test_hasFeature.xhtml} | 11 +- .../Ms2ger/test_historical.html.json | 1 - dom/interfaces/core/nsIDOMNode.idl | 5 +- .../mochitest/dom-level2-core/Makefile.in | 13 -- .../dom-level2-core/test_isSupported04.html | 127 -------------- .../dom-level2-core/test_isSupported05.html | 127 -------------- .../dom-level2-core/test_isSupported06.html | 127 -------------- .../dom-level2-core/test_isSupported07.html | 127 -------------- .../dom-level2-core/test_isSupported09.html | 127 -------------- .../dom-level2-core/test_isSupported10.html | 127 -------------- .../dom-level2-core/test_isSupported11.html | 127 -------------- .../dom-level2-core/test_isSupported12.html | 155 ------------------ .../dom-level2-core/test_isSupported13.html | 115 ------------- .../dom-level2-core/test_isSupported14.html | 117 ------------- .../test_nodeissupported01.html | 149 ----------------- .../test_nodeissupported02.html | 148 ----------------- .../test_nodeissupported04.html | 119 -------------- .../mochitest/dom-level2-html/Makefile.in | 12 -- .../test_HTMLBodyElement07.html | 130 --------------- .../test_HTMLBodyElement08.html | 129 --------------- .../test_HTMLBodyElement09.html | 132 --------------- .../test_HTMLBodyElement10.html | 131 --------------- .../test_HTMLBodyElement11.html | 130 --------------- .../test_HTMLBodyElement12.html | 129 --------------- .../dom-level2-html/test_HTMLDocument22.html | 127 -------------- .../dom-level2-html/test_HTMLDocument23.html | 126 -------------- .../dom-level2-html/test_HTMLDocument24.html | 129 --------------- .../dom-level2-html/test_HTMLDocument25.html | 128 --------------- .../dom-level2-html/test_HTMLDocument26.html | 127 -------------- .../dom-level2-html/test_HTMLDocument27.html | 126 -------------- dom/webidl/Node.webidl | 3 - js/xpconnect/crashtests/468552-1.html | 2 +- 37 files changed, 8 insertions(+), 3303 deletions(-) rename content/svg/content/test/{test_isSupported.xhtml => test_hasFeature.xhtml} (92%) delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported04.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported05.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported06.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported07.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported09.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported10.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported11.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported12.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported13.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_isSupported14.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_nodeissupported01.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_nodeissupported02.html delete mode 100644 dom/tests/mochitest/dom-level2-core/test_nodeissupported04.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement07.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement08.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement09.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement10.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement11.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement12.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLDocument22.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLDocument23.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLDocument24.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLDocument25.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLDocument26.html delete mode 100644 dom/tests/mochitest/dom-level2-html/test_HTMLDocument27.html diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index 98e5943f081..d168c1702cd 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -1559,7 +1559,6 @@ public: nsINode* RemoveChild(nsINode& aChild, mozilla::ErrorResult& aError); already_AddRefed CloneNode(bool aDeep, mozilla::ErrorResult& aError); bool IsEqualNode(nsINode* aNode); - bool IsSupported(const nsAString& aFeature, const nsAString& aVersion); void GetNamespaceURI(nsAString& aNamespaceURI) const { mNodeInfo->GetNamespaceURI(aNamespaceURI); @@ -2003,11 +2002,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsINode, NS_INODE_IID) nsINode::Normalize(); \ return NS_OK; \ } \ - NS_IMETHOD IsSupported(const nsAString& aFeature, const nsAString& aVersion, bool* aResult) __VA_ARGS__ \ - { \ - *aResult = nsINode::IsSupported(aFeature, aVersion); \ - return NS_OK; \ - } \ NS_IMETHOD GetNamespaceURI(nsAString& aNamespaceURI) __VA_ARGS__ \ { \ nsINode::GetNamespaceURI(aNamespaceURI); \ diff --git a/content/base/src/nsINode.cpp b/content/base/src/nsINode.cpp index aa2effaef5f..b8ead9005eb 100644 --- a/content/base/src/nsINode.cpp +++ b/content/base/src/nsINode.cpp @@ -2383,12 +2383,6 @@ nsINode::WrapObject(JSContext *aCx, JSObject *aScope) return obj; } -bool -nsINode::IsSupported(const nsAString& aFeature, const nsAString& aVersion) -{ - return nsContentUtils::InternalIsSupported(this, aFeature, aVersion); -} - already_AddRefed nsINode::CloneNode(bool aDeep, ErrorResult& aError) { diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index a3bc1c21254..a2047789710 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -1070,16 +1070,6 @@ nsSVGElement::sMaskMap[] = { { nullptr } }; -//---------------------------------------------------------------------- -// nsIDOMNode methods - -NS_IMETHODIMP -nsSVGElement::IsSupported(const nsAString& aFeature, const nsAString& aVersion, bool* aReturn) -{ - *aReturn = Element::IsSupported(aFeature, aVersion); - return NS_OK; -} - //---------------------------------------------------------------------- // nsIDOMElement methods diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h index 0189b65446e..a5d51ad36dd 100644 --- a/content/svg/content/src/nsSVGElement.h +++ b/content/svg/content/src/nsSVGElement.h @@ -117,10 +117,6 @@ public: static const MappedAttributeEntry sLightingEffectsMap[]; static const MappedAttributeEntry sMaskMap[]; - // nsIDOMNode - NS_IMETHOD IsSupported(const nsAString& aFeature, const nsAString& aVersion, - bool* aReturn); - // nsIDOMSVGElement NS_IMETHOD GetId(nsAString & aId); NS_IMETHOD SetId(const nsAString & aId); diff --git a/content/svg/content/test/Makefile.in b/content/svg/content/test/Makefile.in index 56b1b7ab2a4..eb025e185d3 100644 --- a/content/svg/content/test/Makefile.in +++ b/content/svg/content/test/Makefile.in @@ -42,7 +42,7 @@ MOCHITEST_FILES = \ test_getElementById.xhtml \ test_getSubStringLength.xhtml \ getSubStringLength-helper.svg \ - test_isSupported.xhtml \ + test_hasFeature.xhtml \ $(filter disabled-for-intermittent-failures--bug-701060, test_lang.xhtml) \ test_nonAnimStrings.xhtml \ test_pathAnimInterpolation.xhtml \ diff --git a/content/svg/content/test/test_isSupported.xhtml b/content/svg/content/test/test_hasFeature.xhtml similarity index 92% rename from content/svg/content/test/test_isSupported.xhtml rename to content/svg/content/test/test_hasFeature.xhtml index 31d0e280a2b..03e1dba5354 100644 --- a/content/svg/content/test/test_isSupported.xhtml +++ b/content/svg/content/test/test_hasFeature.xhtml @@ -3,7 +3,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=519107 --> - Test for availability of svgElement::isSupported + Test for availability of svgElement::hasFeature @@ -135,19 +135,18 @@ var features = [ ["org.w3c.dom.svg.dynamic", "1.0", false] ]; -function testIsSupported(elem) { +function testHasFeature() { for (var [feature, version, accepted_result] of features) { if (accepted_result) { - ok(elem.isSupported(feature, version), "isSupported(" + feature + ", " + version + ") returned wrong value, Was it changed to unsupported feature?"); + ok(document.implementation.hasFeature(feature, version), "hasFeature(" + feature + ", " + version + ") returned wrong value, Was it changed to unsupported feature?"); } else { - todo(elem.isSupported(feature, version), "isSupported(" + feature + ", " + version + ") is unsupported feature string"); + todo(document.implementation.hasFeature(feature, version), "hasFeature(" + feature + ", " + version + ") is unsupported feature string"); } } } function main() { - var svg = document.getElementById("svg"); - testIsSupported(svg); + testHasFeature(); SimpleTest.finish(); } diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_historical.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_historical.html.json index 7ae461a0544..9e972423931 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_historical.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_historical.html.json @@ -11,7 +11,6 @@ "Historical DOM features must be removed: removeAttributeNode": true, "DocumentType member must be nuked: internalSubset": true, "Node member must be nuked: hasAttributes": true, - "Node member must be nuked: isSupported": true, "Node member must be nuked: getUserData": true, "Node member must be nuked: setUserData": true } diff --git a/dom/interfaces/core/nsIDOMNode.idl b/dom/interfaces/core/nsIDOMNode.idl index 9185b45fa4e..2819878d4e8 100644 --- a/dom/interfaces/core/nsIDOMNode.idl +++ b/dom/interfaces/core/nsIDOMNode.idl @@ -18,7 +18,7 @@ interface nsIDOMUserDataHandler; * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */ -[scriptable, uuid(d34c0ab9-21e7-42eb-bb58-9db8ccb66268)] +[scriptable, uuid(a8cae9dc-bd35-4a99-a8e9-4170f8a38566)] interface nsIDOMNode : nsISupports { const unsigned short ELEMENT_NODE = 1; @@ -65,9 +65,6 @@ interface nsIDOMNode : nsISupports // Modified in DOM Level 2: void normalize(); // Introduced in DOM Level 2: - boolean isSupported(in DOMString feature, - in DOMString version); - // Introduced in DOM Level 2: readonly attribute DOMString namespaceURI; // Modified in DOM Core readonly attribute DOMString prefix; diff --git a/dom/tests/mochitest/dom-level2-core/Makefile.in b/dom/tests/mochitest/dom-level2-core/Makefile.in index 774bb427ebd..e4fb99ac7c3 100644 --- a/dom/tests/mochitest/dom-level2-core/Makefile.in +++ b/dom/tests/mochitest/dom-level2-core/Makefile.in @@ -188,16 +188,6 @@ MOCHITEST_FILES_D = \ test_importNode16.html \ test_importNode17.html \ test_internalSubset01.html \ - test_isSupported04.html \ - test_isSupported05.html \ - test_isSupported06.html \ - test_isSupported07.html \ - test_isSupported09.html \ - test_isSupported10.html \ - test_isSupported11.html \ - test_isSupported12.html \ - test_isSupported13.html \ - test_isSupported14.html \ $(NULL) MOCHITEST_FILES_E = \ @@ -244,9 +234,6 @@ MOCHITEST_FILES_E = \ test_nodehasattributes02.html \ test_nodehasattributes03.html \ test_nodehasattributes04.html \ - test_nodeissupported01.html \ - test_nodeissupported02.html \ - test_nodeissupported04.html \ $(NULL) MOCHITEST_FILES_F = \ diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported04.html b/dom/tests/mochitest/dom-level2-core/test_isSupported04.html deleted file mode 100644 index bde7dffaf56..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported04.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported04 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported04

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported05.html b/dom/tests/mochitest/dom-level2-core/test_isSupported05.html deleted file mode 100644 index 203dd1aaa4f..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported05.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported05 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported05

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported06.html b/dom/tests/mochitest/dom-level2-core/test_isSupported06.html deleted file mode 100644 index 48968aa9a1d..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported06.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported06 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported06

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported07.html b/dom/tests/mochitest/dom-level2-core/test_isSupported07.html deleted file mode 100644 index ae05527f6bd..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported07.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported07 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported07

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported09.html b/dom/tests/mochitest/dom-level2-core/test_isSupported09.html deleted file mode 100644 index cb28bc08bc5..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported09.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported09 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported09

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported10.html b/dom/tests/mochitest/dom-level2-core/test_isSupported10.html deleted file mode 100644 index 29eec044523..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported10.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported10 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported10

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported11.html b/dom/tests/mochitest/dom-level2-core/test_isSupported11.html deleted file mode 100644 index b4b8a0276d3..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported11.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported11 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported11

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported12.html b/dom/tests/mochitest/dom-level2-core/test_isSupported12.html deleted file mode 100644 index b459c8fffe1..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported12.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported12 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported12

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported13.html b/dom/tests/mochitest/dom-level2-core/test_isSupported13.html deleted file mode 100644 index 099b5ecad45..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported13.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported13 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported13

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_isSupported14.html b/dom/tests/mochitest/dom-level2-core/test_isSupported14.html deleted file mode 100644 index 3889ba3c0fe..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_isSupported14.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported14 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/isSupported14

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_nodeissupported01.html b/dom/tests/mochitest/dom-level2-core/test_nodeissupported01.html deleted file mode 100644 index 5e9255afa2a..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_nodeissupported01.html +++ /dev/null @@ -1,149 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/nodeissupported01 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/nodeissupported01

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_nodeissupported02.html b/dom/tests/mochitest/dom-level2-core/test_nodeissupported02.html deleted file mode 100644 index 9004cdeadf4..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_nodeissupported02.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/nodeissupported02 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/nodeissupported02

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-core/test_nodeissupported04.html b/dom/tests/mochitest/dom-level2-core/test_nodeissupported04.html deleted file mode 100644 index 72a3d499337..00000000000 --- a/dom/tests/mochitest/dom-level2-core/test_nodeissupported04.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/core/nodeissupported04 - - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/core/nodeissupported04

-

-

-Copyright (c) 2001-2004 World Wide Web Consortium, -(Massachusetts Institute of Technology, European Research Consortium -for Informatics and Mathematics, Keio University). All -Rights Reserved. This work is distributed under the W3C(r) Software License in the -hope that it will be useful, but WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -

- - diff --git a/dom/tests/mochitest/dom-level2-html/Makefile.in b/dom/tests/mochitest/dom-level2-html/Makefile.in index 6c4e8a93323..09d51a24099 100644 --- a/dom/tests/mochitest/dom-level2-html/Makefile.in +++ b/dom/tests/mochitest/dom-level2-html/Makefile.in @@ -65,12 +65,6 @@ MOCHITEST_FILES_A = \ test_HTMLBodyElement04.html \ test_HTMLBodyElement05.html \ test_HTMLBodyElement06.html \ - test_HTMLBodyElement07.html \ - test_HTMLBodyElement08.html \ - test_HTMLBodyElement09.html \ - test_HTMLBodyElement10.html \ - test_HTMLBodyElement11.html \ - test_HTMLBodyElement12.html \ test_HTMLBRElement01.html \ test_HTMLButtonElement01.html \ test_HTMLButtonElement02.html \ @@ -120,12 +114,6 @@ MOCHITEST_FILES_B = \ test_HTMLDocument19.html \ test_HTMLDocument20.html \ test_HTMLDocument21.html \ - test_HTMLDocument22.html \ - test_HTMLDocument23.html \ - test_HTMLDocument24.html \ - test_HTMLDocument25.html \ - test_HTMLDocument26.html \ - test_HTMLDocument27.html \ test_HTMLElement01.html \ test_HTMLElement02.html \ test_HTMLElement03.html \ diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement07.html b/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement07.html deleted file mode 100644 index bc447288a31..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement07.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement07 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement07

-

<test name='HTMLBodyElement07' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLBodyElement07</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("hTmL", null) returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-62018039'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='body' type='Element'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' isNull='true'/>
     <load var='doc' href='document' willBeModified='false'/>
     <body var='body' obj='doc'/>
     <isSupported var='state' obj='body' feature='"hTmL"' version='version'/>
     <assertTrue actual='state' id='isSupportedHTML'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement08.html b/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement08.html deleted file mode 100644 index fbce18ed66b..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement08.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement08 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement08

-

<test name='HTMLBodyElement08' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLBodyElement08</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("hTmL", "2.0") returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-62018039'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='body' type='Element'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' value='"2.0"'/>
     <load var='doc' href='document' willBeModified='false'/>
     <body var='body' obj='doc'/>
     <isSupported var='state' obj='body' feature='"hTmL"' version='version'/>
     <assertTrue actual='state' id='isSupportedHTML'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement09.html b/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement09.html deleted file mode 100644 index f7d80aa2168..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement09.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement09 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement09

-

<test name='HTMLBodyElement09' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLBodyElement09</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("xhTmL", null) returns true if hasFeature("XML", null) is true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-62018039'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='body' type='Element'/>
     <var name='state' type='boolean'/>
     <var name='hasXML' type='boolean'/>
     <var name='version' type='DOMString' isNull='true'/>
     <load var='doc' href='document' willBeModified='false'/>
     <body var='body' obj='doc'/>
     <isSupported var='hasXML' obj='body' feature='"XML"' version='version'/>
     <isSupported var='state' obj='body' feature='"xhTmL"' version='version'/>
     <assertEquals actual='state' expected='hasXML' id='isSupportedXHTML' ignoreCase='false'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement10.html b/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement10.html deleted file mode 100644 index baebd684007..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement10.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement10 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement10

-

<test name='HTMLBodyElement10' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLBodyElement10</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("xhTmL", "2.0") returns true if hasFeature("XML", "2.0") is true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-62018039'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='body' type='Element'/>
     <var name='state' type='boolean'/>
     <var name='hasXML' type='boolean'/>
     <var name='version' type='DOMString' value='"2.0"'/>
     <load var='doc' href='document' willBeModified='false'/>
     <body var='body' obj='doc'/>
     <isSupported var='hasXML' obj='body' feature='"XML"' version='version'/>
     <isSupported var='state' obj='body' feature='"xhTmL"' version='version'/>
     <assertEquals actual='state' expected='hasXML' id='isSupportedXHTML' ignoreCase='false'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement11.html b/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement11.html deleted file mode 100644 index 64830cf3f17..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement11.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement11 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement11

-

<test name='HTMLBodyElement11' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLBodyElement11</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("cOrE", null) returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-62018039'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='body' type='Element'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' isNull='true'/>
     <load var='doc' href='document' willBeModified='false'/>
     <body var='body' obj='doc'/>
     <isSupported var='state' obj='body' feature='"cOrE"' version='version'/>
     <assertTrue actual='state' id='isSupportedCore'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement12.html b/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement12.html deleted file mode 100644 index 05a34b815da..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement12.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement12 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement12

-

<test name='HTMLBodyElement12' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLBodyElement12</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("cOrE", "2.0") returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-62018039'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='body' type='Element'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' value='"2.0"'/>
     <load var='doc' href='document' willBeModified='false'/>
     <body var='body' obj='doc'/>
     <isSupported var='state' obj='body' feature='"cOrE"' version='version'/>
     <assertTrue actual='state' id='isSupportedCore'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument22.html b/dom/tests/mochitest/dom-level2-html/test_HTMLDocument22.html deleted file mode 100644 index 9e269719c86..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument22.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument22 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument22

-

<test name='HTMLDocument22' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLDocument22</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("hTmL", null) returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-26809268'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' isNull='true'/>
     <load var='doc' href='document' willBeModified='true'/>
     <isSupported var='state' obj='doc' feature='"hTmL"' version='version'/>
     <assertTrue actual='state' id='isSupportedHTML'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument23.html b/dom/tests/mochitest/dom-level2-html/test_HTMLDocument23.html deleted file mode 100644 index 89fdf0b83ab..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument23.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument23 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument23

-

<test name='HTMLDocument23' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLDocument23</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("hTmL", "2.0") returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-26809268'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' value='"2.0"'/>
     <load var='doc' href='document' willBeModified='true'/>
     <isSupported var='state' obj='doc' feature='"hTmL"' version='version'/>
     <assertTrue actual='state' id='isSupportedHTML'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument24.html b/dom/tests/mochitest/dom-level2-html/test_HTMLDocument24.html deleted file mode 100644 index a3fb5545385..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument24.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument24 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument24

-

<test name='HTMLDocument24' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLDocument24</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("xhTmL", null) returns true if hasFeature("XML", null) is true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-26809268'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='state' type='boolean'/>
     <var name='hasXML' type='boolean'/>
     <var name='version' type='DOMString' isNull='true'/>
     <load var='doc' href='document' willBeModified='true'/>
     <isSupported var='hasXML' obj='doc' feature='"XML"' version='version'/>
     <isSupported var='state' obj='doc' feature='"xhTmL"' version='version'/>
     <assertEquals actual='state' expected='hasXML' id='isSupportedXHTML' ignoreCase='false'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument25.html b/dom/tests/mochitest/dom-level2-html/test_HTMLDocument25.html deleted file mode 100644 index 8882d2d68bc..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument25.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument25 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument25

-

<test name='HTMLDocument25' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLDocument25</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("xhTmL", "2.0") returns true if hasFeature("XML", "2.0") is true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-26809268'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='state' type='boolean'/>
     <var name='hasXML' type='boolean'/>
     <var name='version' type='DOMString' value='"2.0"'/>
     <load var='doc' href='document' willBeModified='true'/>
     <isSupported var='hasXML' obj='doc' feature='"XML"' version='version'/>
     <isSupported var='state' obj='doc' feature='"xhTmL"' version='version'/>
     <assertEquals actual='state' expected='hasXML' id='isSupportedXHTML' ignoreCase='false'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument26.html b/dom/tests/mochitest/dom-level2-html/test_HTMLDocument26.html deleted file mode 100644 index 6ea84919fbb..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument26.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument26 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument26

-

<test name='HTMLDocument26' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLDocument26</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("cOrE", null) returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-26809268'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' isNull='true'/>
     <load var='doc' href='document' willBeModified='true'/>
     <isSupported var='state' obj='doc' feature='"cOrE"' version='version'/>
     <assertTrue actual='state' id='isSupportedCore'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument27.html b/dom/tests/mochitest/dom-level2-html/test_HTMLDocument27.html deleted file mode 100644 index 49976da6eef..00000000000 --- a/dom/tests/mochitest/dom-level2-html/test_HTMLDocument27.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - -http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument27 - - - - - - -

Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLDocument27

-

<test name='HTMLDocument27' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-2 dom2.xsd'>
     <metadata>
          <title>HTMLDocument27</title> -
          <creator>Curt Arnold</creator> -
          <description> -Checks that Node.isSupported("cOrE", "2.0") returns true. -</description> -
          <date qualifier='created'>2002-03-18</date> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-Core/core#Level-2-Core-Node-supports'/> -
          <subject resource='http://www.w3.org/TR/DOM-Level-2-HTML/html#ID-26809268'/> -
</metadata>
     <var name='doc' type='Document'/>
     <var name='state' type='boolean'/>
     <var name='version' type='DOMString' value='"2.0"'/>
     <load var='doc' href='document' willBeModified='true'/>
     <isSupported var='state' obj='doc' feature='"cOrE"' version='version'/>
     <assertTrue actual='state' id='isSupportedCore'/>
</test>
-

-

- Copyright (c) 2001-2004 World Wide Web Consortium, - (Massachusetts Institute of Technology, Institut National de - Recherche en Informatique et en Automatique, Keio University). All - Rights Reserved. This program is distributed under the W3C's Software - Intellectual Property License. This program is distributed in the - hope that it will be useful, but WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. -

-

See W3C License http://www.w3.org/Consortium/Legal/ - for more details.

- -
- - diff --git a/dom/webidl/Node.webidl b/dom/webidl/Node.webidl index db383d6672b..42e72059064 100644 --- a/dom/webidl/Node.webidl +++ b/dom/webidl/Node.webidl @@ -98,9 +98,6 @@ interface Node : EventTarget { [Constant] readonly attribute DOMString? localName; - // This has been removed from the spec. - boolean isSupported(DOMString feature, DOMString version); - boolean hasAttributes(); [Throws, Func="nsINode::ShouldExposeUserData"] any setUserData(DOMString key, any data, UserDataHandler? handler); diff --git a/js/xpconnect/crashtests/468552-1.html b/js/xpconnect/crashtests/468552-1.html index d6da98209d5..0ce2e3eb406 100644 --- a/js/xpconnect/crashtests/468552-1.html +++ b/js/xpconnect/crashtests/468552-1.html @@ -7,7 +7,7 @@ function boom() var dp = document.__proto__; dp.__proto__ = new XPCNativeWrapper(new XMLSerializer); try { - dp.isSupported(); + dp.replaceChild(); } catch(e) { } } From cb0307099df2f0a1ddd6332faeb899d82a42307b Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 5 Mar 2013 17:27:37 +0100 Subject: [PATCH 067/202] Bug 847918 - `install` command for mach. r=gps --- python/mozbuild/mozbuild/mach_commands.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index a71b6c0e90a..702fd081128 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -310,6 +310,14 @@ class Package(MachCommandBase): def package(self): return self._run_make(directory=".", target='package', ensure_exit_code=False) +@CommandProvider +class Install(MachCommandBase): + """Install a package.""" + + @Command('install', help='Install the package on the machine, or on a device.') + def install(self): + return self._run_make(directory=".", target='install', ensure_exit_code=False) + @CommandProvider class Buildsymbols(MachCommandBase): """Produce a package of debug symbols suitable for use with Breakpad.""" From be83be40c112fde4fb8e8b269e080f7446c2d5c8 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 14 Mar 2013 21:19:24 +0100 Subject: [PATCH 068/202] Bug 851180 - Ensure AndroidGeckoEvent::mAckNeeded is always initialized. r=gbrown --- widget/android/AndroidJavaWrappers.cpp | 11 ++++------- widget/android/AndroidJavaWrappers.h | 5 ----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 941d92668b0..8b5a07716c1 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -487,6 +487,7 @@ void AndroidGeckoEvent::Init(int aType, nsIntRect const& aRect) { mType = aType; + mAckNeeded = false; mRect = aRect; } @@ -641,28 +642,24 @@ void AndroidGeckoEvent::Init(int aType) { mType = aType; + mAckNeeded = false; } void AndroidGeckoEvent::Init(int aType, int aAction) { mType = aType; + mAckNeeded = false; mAction = aAction; } -void -AndroidGeckoEvent::Init(int x1, int y1, int x2, int y2) -{ - mType = DRAW; - mRect.SetEmpty(); -} - void AndroidGeckoEvent::Init(AndroidGeckoEvent *aResizeEvent) { NS_ASSERTION(aResizeEvent->Type() == SIZE_CHANGED, "Init called on non-SIZE_CHANGED event"); mType = FORCED_RESIZE; + mAckNeeded = false; mTime = aResizeEvent->mTime; mPoints = aResizeEvent->mPoints; // x,y coordinates } diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index 6d8fc601afe..aafeeb3a4ed 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -593,16 +593,12 @@ class AndroidGeckoEvent : public WrappedJavaObject public: static void InitGeckoEventClass(JNIEnv *jEnv); - AndroidGeckoEvent() { } AndroidGeckoEvent(int aType) { Init(aType); } AndroidGeckoEvent(int aType, int aAction) { Init(aType, aAction); } - AndroidGeckoEvent(int x1, int y1, int x2, int y2) { - Init(x1, y1, x2, y2); - } AndroidGeckoEvent(int aType, const nsIntRect &aRect) { Init(aType, aRect); } @@ -616,7 +612,6 @@ public: void Init(JNIEnv *jenv, jobject jobj); void Init(int aType); void Init(int aType, int aAction); - void Init(int x1, int y1, int x2, int y2); void Init(int aType, const nsIntRect &aRect); void Init(AndroidGeckoEvent *aResizeEvent); From e8bde7cd7b612379f0c63f89df58df83747a8de2 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 14 Mar 2013 23:46:43 +0100 Subject: [PATCH 069/202] Bug 827347 - Update entity name to go with string change. r=mfinkle --- mobile/android/chrome/content/about.xhtml | 2 +- mobile/android/locales/en-US/chrome/about.dtd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/android/chrome/content/about.xhtml b/mobile/android/chrome/content/about.xhtml index d1a56b83b36..78fbeb199d4 100644 --- a/mobile/android/chrome/content/about.xhtml +++ b/mobile/android/chrome/content/about.xhtml @@ -34,7 +34,7 @@ &aboutPage.checkForUpdates.link; &aboutPage.checkForUpdates.checking; &aboutPage.checkForUpdates.none; - &aboutPage.checkForUpdates.found; + &aboutPage.checkForUpdates.available; &aboutPage.checkForUpdates.downloading; &aboutPage.checkForUpdates.downloaded; diff --git a/mobile/android/locales/en-US/chrome/about.dtd b/mobile/android/locales/en-US/chrome/about.dtd index ae9f27f5b0b..ac0fd7b13d2 100644 --- a/mobile/android/locales/en-US/chrome/about.dtd +++ b/mobile/android/locales/en-US/chrome/about.dtd @@ -9,7 +9,7 @@ - + From 8a624448289458857e1d1e830e59613dcb5d8376 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 14 Mar 2013 23:47:35 +0100 Subject: [PATCH 070/202] Bug 850875 - Set the intent-specified gecko environment variables in setupGeckoEnvironment rather than loadMozglue. r=blassey --- .../android/base/mozglue/GeckoLoader.java.in | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/mobile/android/base/mozglue/GeckoLoader.java.in b/mobile/android/base/mozglue/GeckoLoader.java.in index 637b11777df..575faea7655 100644 --- a/mobile/android/base/mozglue/GeckoLoader.java.in +++ b/mobile/android/base/mozglue/GeckoLoader.java.in @@ -119,7 +119,20 @@ public final class GeckoLoader { return tmpDir; } - public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) { + public static void setupGeckoEnvironment(Activity context, String[] pluginDirs, String profilePath) { + // if we have an intent (we're being launched by an activity) + // read in any environmental variables from it here + Intent intent = context.getIntent(); + if (intent != null) { + String env = intent.getStringExtra("env0"); + Log.d(LOGTAG, "Gecko environment env0: " + env); + for (int c = 1; env != null; c++) { + putenv(env); + env = intent.getStringExtra("env" + c); + Log.d(LOGTAG, "env" + c + ": " + env); + } + } + setupPluginEnvironment(context, pluginDirs); setupDownloadEnvironment(context); @@ -167,7 +180,7 @@ public final class GeckoLoader { Log.d(LOGTAG, "Unable to set the user serial number", e); } - putLocaleEnv(); + setupLocaleEnvironment(); } private static void loadLibsSetup(Context context) { @@ -240,24 +253,6 @@ public final class GeckoLoader { } System.loadLibrary("mozglue"); - - // When running TestPasswordProvider, we're being called with - // a GeckoApplication, which is not an Activity - if (!(context instanceof Activity)) - return; - - Intent i = null; - i = ((Activity)context).getIntent(); - - // if we have an intent (we're being launched by an activity) - // read in any environmental variables from it here - String env = i.getStringExtra("env0"); - Log.d(LOGTAG, "Gecko environment env0: "+ env); - for (int c = 1; env != null; c++) { - putenv(env); - env = i.getStringExtra("env" + c); - Log.d(LOGTAG, "env" + c + ": " + env); - } } public static void loadGeckoLibs(Context context, String apkName) { @@ -265,7 +260,7 @@ public final class GeckoLoader { loadGeckoLibsNative(apkName); } - private static void putLocaleEnv() { + private static void setupLocaleEnvironment() { putenv("LANG=" + Locale.getDefault().toString()); NumberFormat nf = NumberFormat.getInstance(); if (nf instanceof DecimalFormat) { From 634630391096fc29d08992b19e2d34b67f26b231 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 15 Mar 2013 11:40:03 +0100 Subject: [PATCH 071/202] Bug 851060 - Move util files into the util/ folder. r=cpeterson --HG-- rename : mobile/android/base/ActivityResultHandler.java => mobile/android/base/util/ActivityResultHandler.java rename : mobile/android/base/ActivityResultHandlerMap.java => mobile/android/base/util/ActivityResultHandlerMap.java rename : mobile/android/base/GeckoBackgroundThread.java => mobile/android/base/util/GeckoBackgroundThread.java rename : mobile/android/base/GeckoEventListener.java => mobile/android/base/util/GeckoEventListener.java rename : mobile/android/base/GeckoEventResponder.java => mobile/android/base/util/GeckoEventResponder.java rename : mobile/android/base/GeckoJarReader.java => mobile/android/base/util/GeckoJarReader.java rename : mobile/android/base/INIParser.java => mobile/android/base/util/INIParser.java rename : mobile/android/base/INISection.java => mobile/android/base/util/INISection.java rename : mobile/android/base/UiAsyncTask.java => mobile/android/base/util/UiAsyncTask.java --- mobile/android/base/Makefile.in | 18 +++++++++--------- .../base/{ => util}/ActivityResultHandler.java | 0 .../{ => util}/ActivityResultHandlerMap.java | 0 .../base/{ => util}/GeckoBackgroundThread.java | 0 .../base/{ => util}/GeckoEventListener.java | 0 .../base/{ => util}/GeckoEventResponder.java | 0 .../base/{ => util}/GeckoJarReader.java | 0 mobile/android/base/{ => util}/INIParser.java | 0 mobile/android/base/{ => util}/INISection.java | 0 .../android/base/{ => util}/UiAsyncTask.java | 0 10 files changed, 9 insertions(+), 9 deletions(-) rename mobile/android/base/{ => util}/ActivityResultHandler.java (100%) rename mobile/android/base/{ => util}/ActivityResultHandlerMap.java (100%) rename mobile/android/base/{ => util}/GeckoBackgroundThread.java (100%) rename mobile/android/base/{ => util}/GeckoEventListener.java (100%) rename mobile/android/base/{ => util}/GeckoEventResponder.java (100%) rename mobile/android/base/{ => util}/GeckoJarReader.java (100%) rename mobile/android/base/{ => util}/INIParser.java (100%) rename mobile/android/base/{ => util}/INISection.java (100%) rename mobile/android/base/{ => util}/UiAsyncTask.java (100%) diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index ece7ef79ddb..bf4d2f693a0 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -26,18 +26,18 @@ MOZGLUE_PP_JAVA_FILES := \ $(NULL) UTIL_JAVA_FILES := \ - ActivityResultHandler.java \ - ActivityResultHandlerMap.java \ - UiAsyncTask.java \ - GeckoBackgroundThread.java \ - GeckoEventListener.java \ - GeckoEventResponder.java \ - GeckoJarReader.java \ - INIParser.java \ - INISection.java \ + util/ActivityResultHandler.java \ + util/ActivityResultHandlerMap.java \ util/EventDispatcher.java \ util/FloatUtils.java \ + util/GeckoBackgroundThread.java \ + util/GeckoEventListener.java \ + util/GeckoEventResponder.java \ + util/GeckoJarReader.java \ + util/INIParser.java \ + util/INISection.java \ util/StringUtils.java \ + util/UiAsyncTask.java \ $(NULL) FENNEC_JAVA_FILES = \ diff --git a/mobile/android/base/ActivityResultHandler.java b/mobile/android/base/util/ActivityResultHandler.java similarity index 100% rename from mobile/android/base/ActivityResultHandler.java rename to mobile/android/base/util/ActivityResultHandler.java diff --git a/mobile/android/base/ActivityResultHandlerMap.java b/mobile/android/base/util/ActivityResultHandlerMap.java similarity index 100% rename from mobile/android/base/ActivityResultHandlerMap.java rename to mobile/android/base/util/ActivityResultHandlerMap.java diff --git a/mobile/android/base/GeckoBackgroundThread.java b/mobile/android/base/util/GeckoBackgroundThread.java similarity index 100% rename from mobile/android/base/GeckoBackgroundThread.java rename to mobile/android/base/util/GeckoBackgroundThread.java diff --git a/mobile/android/base/GeckoEventListener.java b/mobile/android/base/util/GeckoEventListener.java similarity index 100% rename from mobile/android/base/GeckoEventListener.java rename to mobile/android/base/util/GeckoEventListener.java diff --git a/mobile/android/base/GeckoEventResponder.java b/mobile/android/base/util/GeckoEventResponder.java similarity index 100% rename from mobile/android/base/GeckoEventResponder.java rename to mobile/android/base/util/GeckoEventResponder.java diff --git a/mobile/android/base/GeckoJarReader.java b/mobile/android/base/util/GeckoJarReader.java similarity index 100% rename from mobile/android/base/GeckoJarReader.java rename to mobile/android/base/util/GeckoJarReader.java diff --git a/mobile/android/base/INIParser.java b/mobile/android/base/util/INIParser.java similarity index 100% rename from mobile/android/base/INIParser.java rename to mobile/android/base/util/INIParser.java diff --git a/mobile/android/base/INISection.java b/mobile/android/base/util/INISection.java similarity index 100% rename from mobile/android/base/INISection.java rename to mobile/android/base/util/INISection.java diff --git a/mobile/android/base/UiAsyncTask.java b/mobile/android/base/util/UiAsyncTask.java similarity index 100% rename from mobile/android/base/UiAsyncTask.java rename to mobile/android/base/util/UiAsyncTask.java From 48ba402f287bb20f00a224d97aeae8c9ac3ca58f Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 15 Mar 2013 11:52:52 +0100 Subject: [PATCH 072/202] Bug 802130 - Move assertOnThread functions to a new ThreadUtils class. r=mfinkle --- mobile/android/base/GeckoApp.java | 26 ++------ mobile/android/base/GeckoEditable.java | 15 ++--- mobile/android/base/GeckoInputConnection.java | 17 +++--- mobile/android/base/Makefile.in | 1 + mobile/android/base/PromptService.java | 9 +-- mobile/android/base/gfx/GLController.java | 8 +-- .../base/util/GeckoBackgroundThread.java | 11 ++-- mobile/android/base/util/ThreadUtils.java | 61 +++++++++++++++++++ 8 files changed, 98 insertions(+), 50 deletions(-) create mode 100644 mobile/android/base/util/ThreadUtils.java diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 0273ab593b4..e54edf6c032 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -18,6 +18,7 @@ import org.mozilla.gecko.updater.UpdateServiceHelper; import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.GeckoEventResponder; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UiAsyncTask; import org.json.JSONArray; @@ -1372,6 +1373,8 @@ abstract public class GeckoApp ((GeckoApplication)getApplication()).initialize(); mAppContext = this; + ThreadUtils.setUiThread(Thread.currentThread()); + Tabs.getInstance().attachToActivity(this); Favicons.getInstance().attachToContext(this); @@ -1632,6 +1635,7 @@ abstract public class GeckoApp if (!mIsRestoringActivity) { sGeckoThread = new GeckoThread(intent, passedUri); + ThreadUtils.setGeckoThread(sGeckoThread); } if (!ACTION_DEBUG.equals(action) && GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) { @@ -2638,26 +2642,4 @@ abstract public class GeckoApp } return false; } - - public static void assertOnUiThread() { - Thread uiThread = mAppContext.getMainLooper().getThread(); - assertOnThread(uiThread); - } - - public static void assertOnGeckoThread() { - assertOnThread(sGeckoThread); - } - - public static void assertOnThread(Thread expectedThread) { - Thread currentThread = Thread.currentThread(); - long currentThreadId = currentThread.getId(); - long expectedThreadId = expectedThread.getId(); - - if (currentThreadId != expectedThreadId) { - throw new IllegalThreadStateException("Expected thread " + expectedThreadId + " (\"" - + expectedThread.getName() - + "\"), but running on thread " + currentThreadId - + " (\"" + currentThread.getName() + ")"); - } - } } diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index c9dbaacea60..2bb6b8e8e10 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -7,6 +7,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.InputConnectionHandler; import org.mozilla.gecko.gfx.LayerView; +import org.mozilla.gecko.util.ThreadUtils; import android.os.Build; import android.os.Handler; @@ -233,7 +234,7 @@ final class GeckoEditable void poll() { if (DEBUG) { - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); } if (mActions.isEmpty()) { throw new IllegalStateException("empty actions queue"); @@ -251,7 +252,7 @@ final class GeckoEditable Action peek() { if (DEBUG) { - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); } if (mActions.isEmpty()) { throw new IllegalStateException("empty actions queue"); @@ -304,7 +305,7 @@ final class GeckoEditable } private void assertOnIcThread() { - GeckoApp.assertOnThread(mIcRunHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(mIcRunHandler.getLooper().getThread()); } private void geckoPostToIc(Runnable runnable) { @@ -598,7 +599,7 @@ final class GeckoEditable private void geckoActionReply() { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); } final Action action = mActionQueue.peek(); @@ -650,7 +651,7 @@ final class GeckoEditable public void notifyIME(final int type, final int state) { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); // NOTIFY_IME_REPLY_EVENT is logged separately, inside geckoActionReply() if (type != NOTIFY_IME_REPLY_EVENT) { Log.d(LOGTAG, "notifyIME(" + @@ -727,7 +728,7 @@ final class GeckoEditable public void onSelectionChange(final int start, final int end) { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); Log.d(LOGTAG, "onSelectionChange(" + start + ", " + end + ")"); } if (start < 0 || start > mText.length() || end < 0 || end > mText.length()) { @@ -777,7 +778,7 @@ final class GeckoEditable final int unboundedOldEnd, final int unboundedNewEnd) { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); Log.d(LOGTAG, "onTextChange(\"" + text + "\", " + start + ", " + unboundedOldEnd + ", " + unboundedNewEnd + ")"); } diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index d2a001eca0f..06407d1630a 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.InputConnectionHandler; +import org.mozilla.gecko.util.ThreadUtils; import android.R; import android.content.Context; @@ -45,14 +46,14 @@ class GeckoInputConnection private static Handler sBackgroundHandler; - private class ThreadUtils { + private class InputThreadUtils { private Editable mUiEditable; private Object mUiEditableReturn; private Exception mUiEditableException; private final SynchronousQueue mIcRunnableSync; private final Runnable mIcSignalRunnable; - public ThreadUtils() { + public InputThreadUtils() { mIcRunnableSync = new SynchronousQueue(); mIcSignalRunnable = new Runnable() { @Override public void run() { @@ -62,7 +63,7 @@ class GeckoInputConnection private void runOnIcThread(Handler icHandler, final Runnable runnable) { if (DEBUG) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); Log.d(LOGTAG, "runOnIcThread() on thread " + icHandler.getLooper().getThread().getName()); } @@ -92,7 +93,7 @@ class GeckoInputConnection public void endWaitForUiThread() { if (DEBUG) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); Log.d(LOGTAG, "endWaitForUiThread()"); } try { @@ -103,7 +104,7 @@ class GeckoInputConnection public void waitForUiThread(Handler icHandler) { if (DEBUG) { - GeckoApp.assertOnThread(icHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(icHandler.getLooper().getThread()); Log.d(LOGTAG, "waitForUiThread() blocking on thread " + icHandler.getLooper().getThread().getName()); } @@ -120,7 +121,7 @@ class GeckoInputConnection public Editable getEditableForUiThread(final Handler uiHandler, final Handler icHandler) { if (DEBUG) { - GeckoApp.assertOnThread(uiHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(uiHandler.getLooper().getThread()); } if (icHandler.getLooper() == uiHandler.getLooper()) { // IC thread is UI thread; safe to use Editable directly @@ -136,7 +137,7 @@ class GeckoInputConnection final Method method, final Object[] args) throws Throwable { if (DEBUG) { - GeckoApp.assertOnThread(uiHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(uiHandler.getLooper().getThread()); Log.d(LOGTAG, "UiEditable." + method.getName() + "() blocking"); } synchronized (icHandler) { @@ -177,7 +178,7 @@ class GeckoInputConnection } } - private final ThreadUtils mThreadUtils = new ThreadUtils(); + private final InputThreadUtils mThreadUtils = new InputThreadUtils(); // Managed only by notifyIMEEnabled; see comments in notifyIMEEnabled private int mIMEState; diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index bf4d2f693a0..be391d7f377 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -37,6 +37,7 @@ UTIL_JAVA_FILES := \ util/INIParser.java \ util/INISection.java \ util/StringUtils.java \ + util/ThreadUtils.java \ util/UiAsyncTask.java \ $(NULL) diff --git a/mobile/android/base/PromptService.java b/mobile/android/base/PromptService.java index 908f58fb9f9..d7ad5fedd4a 100644 --- a/mobile/android/base/PromptService.java +++ b/mobile/android/base/PromptService.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.util.GeckoEventResponder; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.widget.DateTimePicker; import org.json.JSONArray; @@ -298,7 +299,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC } public void show(String aTitle, String aText, PromptListItem[] aMenuList, boolean aMultipleSelection) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); // treat actions that show a dialog as if preventDefault by content to prevent panning GeckoApp.mAppContext.getLayerView().abortPanning(); @@ -394,7 +395,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC @Override public void onClick(DialogInterface aDialog, int aWhich) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); JSONObject ret = new JSONObject(); try { int button = -1; @@ -436,13 +437,13 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); mSelected[position] = !mSelected[position]; } @Override public void onCancel(DialogInterface aDialog) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); JSONObject ret = new JSONObject(); try { ret.put("button", -1); diff --git a/mobile/android/base/gfx/GLController.java b/mobile/android/base/gfx/GLController.java index f96d588304c..0360c240f63 100644 --- a/mobile/android/base/gfx/GLController.java +++ b/mobile/android/base/gfx/GLController.java @@ -5,10 +5,10 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.GeckoThread; +import org.mozilla.gecko.util.ThreadUtils; import android.util.Log; @@ -75,7 +75,7 @@ public class GLController { } synchronized void surfaceDestroyed() { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); mSurfaceValid = false; mEGLSurface = null; @@ -94,7 +94,7 @@ public class GLController { } synchronized void surfaceChanged(int newWidth, int newHeight) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); mWidth = newWidth; mHeight = newHeight; @@ -161,7 +161,7 @@ public class GLController { } void createCompositor() { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); if (mCompositorCreated) { // If the compositor has already been created, just resume it instead. We don't need diff --git a/mobile/android/base/util/GeckoBackgroundThread.java b/mobile/android/base/util/GeckoBackgroundThread.java index 739d84337dc..f08f6cb9605 100644 --- a/mobile/android/base/util/GeckoBackgroundThread.java +++ b/mobile/android/base/util/GeckoBackgroundThread.java @@ -35,11 +35,12 @@ public final class GeckoBackgroundThread extends Thread { // Get a Handler for a looper thread, or create one if it doesn't yet exist. public static synchronized Handler getHandler() { if (sHandler == null) { - GeckoBackgroundThread lt = new GeckoBackgroundThread(); - lt.start(); - try { - sHandler = lt.mHandlerQueue.take(); - } catch (InterruptedException ie) {} + GeckoBackgroundThread lt = new GeckoBackgroundThread(); + ThreadUtils.setBackgroundThread(lt); + lt.start(); + try { + sHandler = lt.mHandlerQueue.take(); + } catch (InterruptedException ie) {} } return sHandler; } diff --git a/mobile/android/base/util/ThreadUtils.java b/mobile/android/base/util/ThreadUtils.java new file mode 100644 index 00000000000..76a6b92c063 --- /dev/null +++ b/mobile/android/base/util/ThreadUtils.java @@ -0,0 +1,61 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.util; + +public final class ThreadUtils { + private static Thread sUiThread; + private static Thread sGeckoThread; + private static Thread sBackgroundThread; + + public static void setUiThread(Thread thread) { + sUiThread = thread; + } + + public static void setGeckoThread(Thread thread) { + sGeckoThread = thread; + } + + public static void setBackgroundThread(Thread thread) { + sBackgroundThread = thread; + } + + public static Thread getUiThread() { + return sUiThread; + } + + public static Thread getGeckoThread() { + return sGeckoThread; + } + + public static Thread getBackgroundThread() { + return sBackgroundThread; + } + + public static void assertOnUiThread() { + assertOnThread(getUiThread()); + } + + public static void assertOnGeckoThread() { + assertOnThread(getGeckoThread()); + } + + public static void assertOnBackgroundThread() { + assertOnThread(getBackgroundThread()); + } + + public static void assertOnThread(Thread expectedThread) { + Thread currentThread = Thread.currentThread(); + long currentThreadId = currentThread.getId(); + long expectedThreadId = expectedThread.getId(); + + if (currentThreadId != expectedThreadId) { + throw new IllegalThreadStateException("Expected thread " + expectedThreadId + " (\"" + + expectedThread.getName() + + "\"), but running on thread " + currentThreadId + + " (\"" + currentThread.getName() + ")"); + } + } +} From 1424e8b030657d311713f11a9c42793654ccbdb4 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 15 Mar 2013 11:52:53 +0100 Subject: [PATCH 073/202] Bug 802130 - Move mMainHandler into ThreadUtils. r=mfinkle --- .../android/base/ActivityHandlerHelper.java | 3 +- mobile/android/base/AwesomeBar.java | 5 ++-- mobile/android/base/BrowserApp.java | 29 ++++++++++--------- mobile/android/base/Favicons.java | 5 ++-- mobile/android/base/FormAssistPopup.java | 9 +++--- mobile/android/base/GeckoAccessibility.java | 3 +- mobile/android/base/GeckoApp.java | 25 +++++++--------- mobile/android/base/GeckoAppShell.java | 21 ++++---------- mobile/android/base/GeckoEditable.java | 2 +- mobile/android/base/GeckoPreferences.java | 13 +++++---- mobile/android/base/PromptService.java | 2 +- .../android/base/awesomebar/AllPagesTab.java | 3 +- .../android/base/awesomebar/BookmarksTab.java | 3 +- .../android/base/awesomebar/HistoryTab.java | 3 +- mobile/android/base/gfx/GeckoLayerClient.java | 4 +-- mobile/android/base/gfx/PluginLayer.java | 5 ++-- mobile/android/base/util/ThreadUtils.java | 15 +++++++++- 17 files changed, 80 insertions(+), 70 deletions(-) diff --git a/mobile/android/base/ActivityHandlerHelper.java b/mobile/android/base/ActivityHandlerHelper.java index 1994930b3da..6233184a87d 100644 --- a/mobile/android/base/ActivityHandlerHelper.java +++ b/mobile/android/base/ActivityHandlerHelper.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.util.ActivityResultHandler; import org.mozilla.gecko.util.ActivityResultHandlerMap; +import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONException; import org.json.JSONObject; @@ -152,7 +153,7 @@ class ActivityHandlerHelper { } Runnable filePicker = new FilePickerPromptRunnable(getFilePickerTitle(context, aMimeType), items); - GeckoAppShell.getMainHandler().post(filePicker); + ThreadUtils.postToUiThread(filePicker); String promptServiceResult = ""; try { diff --git a/mobile/android/base/AwesomeBar.java b/mobile/android/base/AwesomeBar.java index f8ed70df8da..b94279bbe62 100644 --- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -7,8 +7,9 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserContract.Combined; -import org.mozilla.gecko.util.UiAsyncTask; import org.mozilla.gecko.util.StringUtils; +import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.gecko.util.UiAsyncTask; import android.app.Activity; import android.app.AlertDialog; @@ -100,7 +101,7 @@ public class AwesomeBar extends GeckoActivity { @Override public void onEditSuggestion(final String text) { - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { mText.setText(text); diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 256e5eb2b08..aac1d807362 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -11,6 +11,7 @@ import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.util.UiAsyncTask; import org.mozilla.gecko.util.GeckoBackgroundThread; +import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONArray; import org.json.JSONException; @@ -137,7 +138,7 @@ abstract public class BrowserApp extends GeckoApp : TabsPanel.Panel.NORMAL_TABS; // Delay calling showTabs so that it does not modify the mTabsChangedListeners // array while we are still iterating through the array. - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (areTabsShown() && mTabsPanel.getCurrentPanel() != panel) @@ -397,7 +398,7 @@ abstract public class BrowserApp extends GeckoApp @Override void onStatePurged() { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (mAboutHomeContent != null) @@ -469,7 +470,7 @@ abstract public class BrowserApp extends GeckoApp } mDynamicToolbarEnabled = value; - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (mDynamicToolbarEnabled) { @@ -603,7 +604,7 @@ abstract public class BrowserApp extends GeckoApp @Override void toggleChrome(final boolean aShow) { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (aShow) { @@ -622,7 +623,7 @@ abstract public class BrowserApp extends GeckoApp @Override void focusChrome() { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { mBrowserToolbar.show(); @@ -751,7 +752,7 @@ abstract public class BrowserApp extends GeckoApp info.parent = message.getInt("parent") + ADDON_MENU_OFFSET; } catch (Exception ex) { } final MenuItemInfo menuItemInfo = info; - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { addAddonMenuItem(menuItemInfo); @@ -759,7 +760,7 @@ abstract public class BrowserApp extends GeckoApp }); } else if (event.equals("Menu:Remove")) { final int id = message.getInt("id") + ADDON_MENU_OFFSET; - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { removeAddonMenuItem(id); @@ -768,7 +769,7 @@ abstract public class BrowserApp extends GeckoApp } else if (event.equals("Menu:Update")) { final int id = message.getInt("id") + ADDON_MENU_OFFSET; final JSONObject options = message.getJSONObject("options"); - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { updateAddonMenuItem(id, options); @@ -804,7 +805,7 @@ abstract public class BrowserApp extends GeckoApp dialog.dismiss(); } }); - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { dialogBuilder.show(); @@ -814,7 +815,7 @@ abstract public class BrowserApp extends GeckoApp final boolean visible = message.getString("visible").equals("true"); GeckoPreferences.setCharEncodingState(visible); final Menu menu = mMenu; - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (menu != null) @@ -834,7 +835,7 @@ abstract public class BrowserApp extends GeckoApp // menuitem, which is specific to BrowserApp. super.handleMessage(event, message); final Menu menu = mMenu; - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (menu != null) @@ -1054,7 +1055,7 @@ abstract public class BrowserApp extends GeckoApp mAboutHomeShowing = true; Runnable r = new AboutHomeRunnable(true); - mMainHandler.postAtFrontOfQueue(r); + ThreadUtils.getUiHandler().postAtFrontOfQueue(r); } private void hideAboutHome() { @@ -1066,7 +1067,7 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.setShadowVisibility(true); mAboutHomeShowing = false; Runnable r = new AboutHomeRunnable(false); - mMainHandler.postAtFrontOfQueue(r); + ThreadUtils.getUiHandler().postAtFrontOfQueue(r); } private class AboutHomeRunnable implements Runnable { @@ -1339,7 +1340,7 @@ abstract public class BrowserApp extends GeckoApp @Override public void setFullScreen(final boolean fullscreen) { super.setFullScreen(fullscreen); - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (fullscreen) diff --git a/mobile/android/base/Favicons.java b/mobile/android/base/Favicons.java index 5c09ef41e39..fa14b073f66 100644 --- a/mobile/android/base/Favicons.java +++ b/mobile/android/base/Favicons.java @@ -6,8 +6,9 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.util.UiAsyncTask; import org.mozilla.gecko.util.GeckoJarReader; +import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.gecko.util.UiAsyncTask; import org.apache.http.HttpEntity; import org.apache.http.client.methods.HttpGet; @@ -82,7 +83,7 @@ public class Favicons { putFaviconInMemCache(pageUrl, image); // We want to always run the listener on UI thread - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (listener != null) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index 7ff85ce082d..187db73414e 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -8,6 +8,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONArray; import org.json.JSONException; @@ -101,7 +102,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene private void handleAutoCompleteMessage(JSONObject message) throws JSONException { final JSONArray suggestions = message.getJSONArray("suggestions"); final JSONObject rect = message.getJSONObject("rect"); - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { showAutoCompleteSuggestions(suggestions, rect); @@ -112,16 +113,16 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene private void handleValidationMessage(JSONObject message) throws JSONException { final String validationMessage = message.getString("validationMessage"); final JSONObject rect = message.getJSONObject("rect"); - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { showValidationMessage(validationMessage, rect); } }); } - + private void handleHideMessage(JSONObject message) { - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { hide(); diff --git a/mobile/android/base/GeckoAccessibility.java b/mobile/android/base/GeckoAccessibility.java index ef563421058..0f724af5e47 100644 --- a/mobile/android/base/GeckoAccessibility.java +++ b/mobile/android/base/GeckoAccessibility.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.LayerView; +import org.mozilla.gecko.util.ThreadUtils; import android.view.accessibility.*; import android.view.View; @@ -161,7 +162,7 @@ public class GeckoAccessibility { // Store the JSON message and use it to populate the event later in the code path. sEventMessage = message; - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { // If this is an accessibility focus, a lot of internal voodoo happens so we perform an diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index e54edf6c032..adad2b9b393 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -157,7 +157,6 @@ abstract public class GeckoApp protected MenuPanel mMenuPanel; protected Menu mMenu; private static GeckoThread sGeckoThread; - public Handler mMainHandler; private GeckoProfile mProfile; public static int mOrientation; protected boolean mIsRestoringActivity; @@ -799,7 +798,7 @@ abstract public class GeckoApp } else if (event.equals("Bookmark:Insert")) { final String url = message.getString("url"); final String title = message.getString("title"); - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { Toast.makeText(GeckoApp.mAppContext, R.string.bookmark_added, Toast.LENGTH_SHORT).show(); @@ -949,7 +948,7 @@ abstract public class GeckoApp } }); - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { Dialog dialog = builder.create(); @@ -967,7 +966,7 @@ abstract public class GeckoApp } public void showToast(final int resId, final int duration) { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { Toast.makeText(mAppContext, resId, duration).show(); @@ -976,7 +975,7 @@ abstract public class GeckoApp } void handleShowToast(final String message, final String duration) { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { Toast toast; @@ -1018,7 +1017,7 @@ abstract public class GeckoApp } void addPluginView(final View view, final Rect rect, final boolean isFullScreen) { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { Tabs tabs = Tabs.getInstance(); @@ -1058,7 +1057,7 @@ abstract public class GeckoApp // We need do do this on the next iteration in order to avoid // a deadlock, see comment below in FullScreenHolder - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { mLayerView.show(); @@ -1075,7 +1074,7 @@ abstract public class GeckoApp } void removePluginView(final View view, final boolean isFullScreen) { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { Tabs tabs = Tabs.getInstance(); @@ -1299,7 +1298,7 @@ abstract public class GeckoApp } public void setFullScreen(final boolean fullscreen) { - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { // Hide/show the system notification bar @@ -1373,7 +1372,7 @@ abstract public class GeckoApp ((GeckoApplication)getApplication()).initialize(); mAppContext = this; - ThreadUtils.setUiThread(Thread.currentThread()); + ThreadUtils.setUiThread(Thread.currentThread(), new Handler()); Tabs.getInstance().attachToActivity(this); Favicons.getInstance().attachToContext(this); @@ -1393,8 +1392,6 @@ abstract public class GeckoApp Telemetry.HistogramAdd("FENNEC_RESTORING_ACTIVITY", 1); } - mMainHandler = new Handler(); - // Fix for bug 830557 on Tegra boards running Froyo. // This fix must be done before doing layout. // Assume the bug is fixed in Gingerbread and up. @@ -1642,7 +1639,7 @@ abstract public class GeckoApp sGeckoThread.start(); } else if (ACTION_DEBUG.equals(action) && GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.WaitForDebugger)) { - mMainHandler.postDelayed(new Runnable() { + ThreadUtils.getUiHandler().postDelayed(new Runnable() { @Override public void run() { GeckoThread.setLaunchState(GeckoThread.LaunchState.Launching); @@ -2547,7 +2544,7 @@ abstract public class GeckoApp */ super.addView(view, index); - mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { mLayerView.hide(); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 62e7c514136..5a3d48f15b4 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -14,6 +14,7 @@ import org.mozilla.gecko.mozglue.GeckoLoader; import org.mozilla.gecko.util.EventDispatcher; import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.ThreadUtils; import android.app.ActivityManager; import android.app.PendingIntent; @@ -147,8 +148,6 @@ public class GeckoAppShell private static boolean mLocationHighAccuracy = false; - private static Handler sGeckoHandler; - static ActivityHandlerHelper sActivityHelper = new ActivityHandlerHelper(); static NotificationServiceClient sNotificationClient; @@ -257,22 +256,12 @@ public class GeckoAppShell } } - // Get a Handler for the main java thread - public static Handler getMainHandler() { - return GeckoApp.mAppContext.mMainHandler; - } - - public static Handler getGeckoHandler() { - return sGeckoHandler; - } - public static Handler getHandler() { return GeckoBackgroundThread.getHandler(); } public static void runGecko(String apkPath, String args, String url, String type) { Looper.prepare(); - sGeckoHandler = new Handler(); // run gecko -- it will spawn its own thread GeckoAppShell.nativeInit(); @@ -409,7 +398,7 @@ public class GeckoAppShell } public static void enableLocation(final boolean enable) { - getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { LocationManager lm = (LocationManager) @@ -1346,7 +1335,7 @@ public class GeckoAppShell } public static void notifyDefaultPrevented(final boolean defaultPrevented) { - getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { LayerView view = GeckoApp.mAppContext.getLayerView(); @@ -1701,7 +1690,7 @@ public class GeckoAppShell static byte[] sCameraBuffer = null; static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) { - getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { try { @@ -1793,7 +1782,7 @@ public class GeckoAppShell } static synchronized void closeCamera() { - getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { try { diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index 2bb6b8e8e10..fbdeb655d7d 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -297,7 +297,7 @@ final class GeckoEditable LayerView v = GeckoApp.mAppContext.getLayerView(); mListener = GeckoInputConnection.create(v, this); - mIcRunHandler = mIcPostHandler = GeckoApp.mAppContext.mMainHandler; + mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler(); } private boolean onIcThread() { diff --git a/mobile/android/base/GeckoPreferences.java b/mobile/android/base/GeckoPreferences.java index 1353732111d..7a36dbcfa75 100644 --- a/mobile/android/base/GeckoPreferences.java +++ b/mobile/android/base/GeckoPreferences.java @@ -7,6 +7,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.background.announcements.AnnouncementsConstants; import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONArray; import org.json.JSONObject; @@ -110,7 +111,7 @@ public class GeckoPreferences boolean success = message.getBoolean("success"); final int stringRes = success ? R.string.private_data_success : R.string.private_data_fail; final Context context = this; - GeckoAppShell.getMainHandler().post(new Runnable () { + ThreadUtils.postToUiThread(new Runnable () { @Override public void run() { Toast.makeText(context, stringRes, Toast.LENGTH_SHORT).show(); @@ -423,7 +424,7 @@ public class GeckoPreferences @Override public void prefValue(String prefName, final boolean value) { final Preference pref = getField(prefName); if (pref instanceof CheckBoxPreference) { - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (((CheckBoxPreference)pref).isChecked() != value) @@ -436,14 +437,14 @@ public class GeckoPreferences @Override public void prefValue(String prefName, final String value) { final Preference pref = getField(prefName); if (pref instanceof EditTextPreference) { - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { ((EditTextPreference)pref).setText(value); } }); } else if (pref instanceof ListPreference) { - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { ((ListPreference)pref).setValue(value); @@ -456,7 +457,7 @@ public class GeckoPreferences final FontSizePreference fontSizePref = (FontSizePreference) pref; fontSizePref.setSavedFontSize(value); final String fontSizeName = fontSizePref.getSavedFontSizeName(); - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { fontSizePref.setSummary(fontSizeName); // Ex: "Small". @@ -467,7 +468,7 @@ public class GeckoPreferences @Override public void finish() { // enable all preferences once we have them from gecko - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { mPreferenceScreen.setEnabled(true); diff --git a/mobile/android/base/PromptService.java b/mobile/android/base/PromptService.java index d7ad5fedd4a..7452414d545 100644 --- a/mobile/android/base/PromptService.java +++ b/mobile/android/base/PromptService.java @@ -273,7 +273,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC @Override public void handleMessage(String event, final JSONObject message) { // The dialog must be created on the UI thread. - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { processMessage(message); diff --git a/mobile/android/base/awesomebar/AllPagesTab.java b/mobile/android/base/awesomebar/AllPagesTab.java index ca44a7bebe4..8c7df57c3a9 100644 --- a/mobile/android/base/awesomebar/AllPagesTab.java +++ b/mobile/android/base/awesomebar/AllPagesTab.java @@ -13,6 +13,7 @@ import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.util.UiAsyncTask; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.StringUtils; +import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONArray; import org.json.JSONException; @@ -718,7 +719,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { @Override public void handleMessage(String event, final JSONObject message) { if (event.equals("SearchEngines:Data")) { - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { setSearchEngines(message); diff --git a/mobile/android/base/awesomebar/BookmarksTab.java b/mobile/android/base/awesomebar/BookmarksTab.java index 347ce8a75fb..315a185a774 100644 --- a/mobile/android/base/awesomebar/BookmarksTab.java +++ b/mobile/android/base/awesomebar/BookmarksTab.java @@ -10,6 +10,7 @@ import org.mozilla.gecko.db.BrowserContract.Bookmarks; import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB.URLColumns; +import org.mozilla.gecko.util.ThreadUtils; import android.app.Activity; import android.content.Context; @@ -368,7 +369,7 @@ public class BookmarksTab extends AwesomeBarTab { @Override protected void onPostExecute(final Cursor cursor) { // Hack: force this to the main thread, even though it should already be on it - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { // this will update the cursorAdapter to use the new one if it already exists diff --git a/mobile/android/base/awesomebar/HistoryTab.java b/mobile/android/base/awesomebar/HistoryTab.java index a39b54cf583..92f0290dcae 100644 --- a/mobile/android/base/awesomebar/HistoryTab.java +++ b/mobile/android/base/awesomebar/HistoryTab.java @@ -9,6 +9,7 @@ import org.mozilla.gecko.AwesomeBar.ContextMenuSubject; import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB.URLColumns; +import org.mozilla.gecko.util.ThreadUtils; import android.app.Activity; import android.content.ContentResolver; @@ -368,7 +369,7 @@ public class HistoryTab extends AwesomeBarTab { final ExpandableListView historyList = (ExpandableListView)getView(); // Hack: force this to the main thread, even though it should already be on it - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { historyList.setAdapter(mCursorAdapter); diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index a6458026bbd..fb213a5a0bc 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -8,12 +8,12 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.BrowserApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; -import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.util.EventDispatcher; import org.mozilla.gecko.util.FloatUtils; +import org.mozilla.gecko.util.ThreadUtils; import android.content.Context; import android.graphics.PointF; @@ -735,7 +735,7 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget } private void setShadowVisibility() { - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (BrowserApp.mBrowserToolbar == null) { diff --git a/mobile/android/base/gfx/PluginLayer.java b/mobile/android/base/gfx/PluginLayer.java index 35fb34b3873..74f165b7772 100644 --- a/mobile/android/base/gfx/PluginLayer.java +++ b/mobile/android/base/gfx/PluginLayer.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.util.FloatUtils; +import org.mozilla.gecko.util.ThreadUtils; import android.graphics.Rect; import android.graphics.RectF; @@ -60,7 +61,7 @@ public class PluginLayer extends TileLayer { private void hideView() { if (mViewVisible) { - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { mView.setVisibility(View.GONE); @@ -71,7 +72,7 @@ public class PluginLayer extends TileLayer { } public void showView() { - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { if (mContainer.indexOfChild(mView) < 0) { diff --git a/mobile/android/base/util/ThreadUtils.java b/mobile/android/base/util/ThreadUtils.java index 76a6b92c063..29d4a4f8734 100644 --- a/mobile/android/base/util/ThreadUtils.java +++ b/mobile/android/base/util/ThreadUtils.java @@ -5,13 +5,18 @@ package org.mozilla.gecko.util; +import android.os.Handler; + public final class ThreadUtils { private static Thread sUiThread; private static Thread sGeckoThread; private static Thread sBackgroundThread; - public static void setUiThread(Thread thread) { + private static Handler sUiHandler; + + public static void setUiThread(Thread thread, Handler handler) { sUiThread = thread; + sUiHandler = handler; } public static void setGeckoThread(Thread thread) { @@ -26,6 +31,14 @@ public final class ThreadUtils { return sUiThread; } + public static Handler getUiHandler() { + return sUiHandler; + } + + public static void postToUiThread(Runnable runnable) { + sUiHandler.post(runnable); + } + public static Thread getGeckoThread() { return sGeckoThread; } From 0c17a97577a8381ac11874ba0eab2f54b82f57a0 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 15 Mar 2013 11:52:53 +0100 Subject: [PATCH 074/202] Bug 802130 - Move GeckoAppShell.getHandler() to ThreadUtils.getBackgroundHandler(). r=mfinkle --- mobile/android/base/AboutHomeContent.java | 13 ++++++------ mobile/android/base/AboutHomePromoBox.java | 5 +++-- .../android/base/AndroidImportPreference.java | 4 ++-- mobile/android/base/AwesomeBar.java | 6 +++--- mobile/android/base/BrowserApp.java | 13 ++++++------ mobile/android/base/Distribution.java | 4 ++-- mobile/android/base/Favicons.java | 2 +- mobile/android/base/GeckoAccessibility.java | 4 ++-- mobile/android/base/GeckoApp.java | 21 +++++++++---------- mobile/android/base/GeckoAppShell.java | 20 +++++++----------- mobile/android/base/GlobalHistory.java | 3 ++- mobile/android/base/MemoryMonitor.java | 11 +++++----- .../android/base/MultiChoicePreference.java | 6 ++++-- mobile/android/base/ProfileMigrator.java | 9 ++++---- mobile/android/base/Tab.java | 11 +++++----- mobile/android/base/Tabs.java | 9 ++++---- mobile/android/base/TabsAccessor.java | 5 +++-- mobile/android/base/WebAppAllocator.java | 6 +++--- .../android/base/awesomebar/AllPagesTab.java | 4 ++-- .../android/base/awesomebar/HistoryTab.java | 2 +- .../android/base/db/BrowserProvider.java.in | 8 +++---- mobile/android/base/db/TabsProvider.java.in | 4 ++-- .../base/util/GeckoBackgroundThread.java | 6 +++--- mobile/android/base/util/ThreadUtils.java | 8 +++++++ 24 files changed, 97 insertions(+), 87 deletions(-) diff --git a/mobile/android/base/AboutHomeContent.java b/mobile/android/base/AboutHomeContent.java index 450c24e5b2d..7268645aa7c 100644 --- a/mobile/android/base/AboutHomeContent.java +++ b/mobile/android/base/AboutHomeContent.java @@ -12,6 +12,7 @@ import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper; import org.mozilla.gecko.sync.setup.SyncAccounts; import org.mozilla.gecko.util.ActivityResultHandler; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UiAsyncTask; import org.json.JSONArray; @@ -143,7 +144,7 @@ public class AboutHomeContent extends ScrollView // Because the tabs URI is coarse grained, this updates the // remote tabs component on *every* tab change // The observer will run on the background thread (see constructor argument) - mTabsContentObserver = new ContentObserver(GeckoAppShell.getHandler()) { + mTabsContentObserver = new ContentObserver(ThreadUtils.getBackgroundHandler()) { @Override public void onChange(boolean selfChange) { update(EnumSet.of(AboutHomeContent.UpdateFlags.REMOTE_TABS)); @@ -426,7 +427,7 @@ public class AboutHomeContent extends ScrollView if (urls.size() == 0) return; - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public Cursor doInBackground(Void... params) { return BrowserDB.getThumbnailsForUrls(cr, urls); @@ -440,7 +441,7 @@ public class AboutHomeContent extends ScrollView } void update(final EnumSet flags) { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { if (flags.contains(UpdateFlags.TOP_SITES)) @@ -990,7 +991,7 @@ public class AboutHomeContent extends ScrollView final String url = holder.getUrl(); // Quickly update the view so that there isn't as much lag between the request and response clearThumbnail(holder); - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public Void doInBackground(Void... params) { final ContentResolver resolver = mActivity.getContentResolver(); @@ -1011,7 +1012,7 @@ public class AboutHomeContent extends ScrollView holder.setPinned(true); // update the database on a background thread - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public Void doInBackground(Void... params) { final ContentResolver resolver = mActivity.getContentResolver(); @@ -1056,7 +1057,7 @@ public class AboutHomeContent extends ScrollView holder.setPinned(true); // update the database on a background thread - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public Bitmap doInBackground(Void... params) { final ContentResolver resolver = mActivity.getContentResolver(); diff --git a/mobile/android/base/AboutHomePromoBox.java b/mobile/android/base/AboutHomePromoBox.java index df8a4f5750b..7cdb65dd35b 100644 --- a/mobile/android/base/AboutHomePromoBox.java +++ b/mobile/android/base/AboutHomePromoBox.java @@ -7,6 +7,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity; import org.mozilla.gecko.sync.setup.SyncAccounts; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UiAsyncTask; import android.accounts.Account; @@ -70,7 +71,7 @@ public class AboutHomePromoBox extends TextView implements View.OnClickListener showRandomPromo(); } }; - AccountManager.get(mContext).addOnAccountsUpdatedListener(mAccountListener, GeckoAppShell.getHandler(), false); + AccountManager.get(mContext).addOnAccountsUpdatedListener(mAccountListener, ThreadUtils.getBackgroundHandler(), false); } @Override public boolean canShow() { @@ -193,7 +194,7 @@ public class AboutHomePromoBox extends TextView implements View.OnClickListener } private void getAvailableTypes(final GetTypesCallback callback) { - (new UiAsyncTask>(GeckoAppShell.getHandler()) { + (new UiAsyncTask>(ThreadUtils.getBackgroundHandler()) { @Override public ArrayList doInBackground(Void... params) { // Run all of this on a background thread diff --git a/mobile/android/base/AndroidImportPreference.java b/mobile/android/base/AndroidImportPreference.java index c9dcae79ea7..045ce7fdbbe 100644 --- a/mobile/android/base/AndroidImportPreference.java +++ b/mobile/android/base/AndroidImportPreference.java @@ -5,7 +5,7 @@ package org.mozilla.gecko; -import org.mozilla.gecko.util.GeckoBackgroundThread; +import org.mozilla.gecko.util.ThreadUtils; import android.app.ProgressDialog; import android.content.Context; @@ -85,7 +85,7 @@ class AndroidImportPreference extends MultiChoicePreference { } }; - GeckoBackgroundThread.getHandler().post( + ThreadUtils.postToBackgroundThread( // Constructing AndroidImport may need finding the profile, // which hits disk, so it needs to go into a Runnable too. new Runnable() { diff --git a/mobile/android/base/AwesomeBar.java b/mobile/android/base/AwesomeBar.java index b94279bbe62..a0a197a5a91 100644 --- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -579,7 +579,7 @@ public class AwesomeBar extends GeckoActivity { editPrompt.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public Void doInBackground(Void... params) { String newUrl = locationText.getText().toString().trim(); @@ -629,7 +629,7 @@ public class AwesomeBar extends GeckoActivity { break; } case R.id.remove_bookmark: { - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { private boolean mInReadingList; @Override @@ -659,7 +659,7 @@ public class AwesomeBar extends GeckoActivity { break; } case R.id.remove_history: { - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public Void doInBackground(Void... params) { BrowserDB.removeHistoryEntry(getContentResolver(), id); diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index aac1d807362..71a1589aa89 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -10,7 +10,6 @@ import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.util.UiAsyncTask; -import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONArray; @@ -377,7 +376,7 @@ abstract public class BrowserApp extends GeckoApp return; } - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { BrowserDB.addReadingListItem(getContentResolver(), title, url); @@ -387,7 +386,7 @@ abstract public class BrowserApp extends GeckoApp } void handleReaderRemoved(final String url) { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { BrowserDB.removeReadingListItemWithURL(getContentResolver(), url); @@ -1198,7 +1197,7 @@ abstract public class BrowserApp extends GeckoApp item.setIcon(drawable); } else if (info.icon.startsWith("jar:") || info.icon.startsWith("file://")) { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { try { @@ -1548,7 +1547,7 @@ abstract public class BrowserApp extends GeckoApp return; } - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public synchronized Boolean doInBackground(Void... params) { // Check to see how many times the app has been launched. @@ -1575,7 +1574,7 @@ abstract public class BrowserApp extends GeckoApp } private void resetFeedbackLaunchCount() { - GeckoBackgroundThread.post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public synchronized void run() { SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE); @@ -1585,7 +1584,7 @@ abstract public class BrowserApp extends GeckoApp } private void getLastUrl() { - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public synchronized String doInBackground(Void... params) { // Get the most recent URL stored in browser history. diff --git a/mobile/android/base/Distribution.java b/mobile/android/base/Distribution.java index 89f554be923..1a09295b2c0 100644 --- a/mobile/android/base/Distribution.java +++ b/mobile/android/base/Distribution.java @@ -9,7 +9,7 @@ package org.mozilla.gecko; -import org.mozilla.gecko.util.GeckoBackgroundThread; +import org.mozilla.gecko.util.ThreadUtils; import android.app.Activity; import android.content.Context; @@ -45,7 +45,7 @@ public final class Distribution { */ public static void init(final Context context, final String packagePath) { // Read/write preferences and files on the background thread. - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { // Bail if we've already initialized the distribution. diff --git a/mobile/android/base/Favicons.java b/mobile/android/base/Favicons.java index fa14b073f66..d8619b1dd95 100644 --- a/mobile/android/base/Favicons.java +++ b/mobile/android/base/Favicons.java @@ -112,7 +112,7 @@ public class Favicons { return -1; } - LoadFaviconTask task = new LoadFaviconTask(GeckoAppShell.getHandler(), pageUrl, faviconUrl, persist, listener); + LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageUrl, faviconUrl, persist, listener); long taskId = task.getId(); mLoadTasks.put(taskId, task); diff --git a/mobile/android/base/GeckoAccessibility.java b/mobile/android/base/GeckoAccessibility.java index 0f724af5e47..80602b8aa10 100644 --- a/mobile/android/base/GeckoAccessibility.java +++ b/mobile/android/base/GeckoAccessibility.java @@ -43,7 +43,7 @@ public class GeckoAccessibility { })); public static void updateAccessibilitySettings () { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { JSONObject ret = new JSONObject(); @@ -130,7 +130,7 @@ public class GeckoAccessibility { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { // Before Jelly Bean we send events directly from here while spoofing the source by setting // the package and class name manually. - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { sendDirectAccessibilityEvent(eventType, message); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index adad2b9b393..75c8e44e773 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -15,7 +15,6 @@ import org.mozilla.gecko.gfx.PluginLayer; import org.mozilla.gecko.gfx.PointUtils; import org.mozilla.gecko.updater.UpdateService; import org.mozilla.gecko.updater.UpdateServiceHelper; -import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.GeckoEventResponder; import org.mozilla.gecko.util.ThreadUtils; @@ -626,7 +625,7 @@ abstract public class GeckoApp } void handleFaviconRequest(final String url) { - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public String doInBackground(Void... params) { return Favicons.getInstance().getFaviconUrlForPageUrl(url); @@ -802,7 +801,7 @@ abstract public class GeckoApp @Override public void run() { Toast.makeText(GeckoApp.mAppContext, R.string.bookmark_added, Toast.LENGTH_SHORT).show(); - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { BrowserDB.addBookmark(GeckoApp.mAppContext.getContentResolver(), title, url); @@ -1104,7 +1103,7 @@ abstract public class GeckoApp notification.setLatestEventInfo(mAppContext, fileName, progText, emptyIntent ); notification.flags |= Notification.FLAG_ONGOING_EVENT; notification.show(); - new UiAsyncTask(GeckoAppShell.getHandler()){ + new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override protected Boolean doInBackground(Void... params) { @@ -1440,7 +1439,7 @@ abstract public class GeckoApp mPrivateBrowsingSession = savedInstanceState.getString(SAVED_STATE_PRIVATE_SESSION); } - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { SharedPreferences prefs = @@ -1545,7 +1544,7 @@ abstract public class GeckoApp Uri data = intent.getData(); if (data != null && "http".equals(data.getScheme())) { startupAction = StartupAction.PREFETCH; - GeckoAppShell.getHandler().post(new PrefetchRunnable(data.toString())); + ThreadUtils.postToBackgroundThread(new PrefetchRunnable(data.toString())); } Tabs.registerOnTabsChangedListener(this); @@ -1706,7 +1705,7 @@ abstract public class GeckoApp // End of the startup of our Java App mJavaUiStartupTimer.stop(); - GeckoAppShell.getHandler().postDelayed(new Runnable() { + ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() { @Override public void run() { // Sync settings need Gecko to be loaded, so @@ -1949,7 +1948,7 @@ abstract public class GeckoApp // User may have enabled/disabled accessibility. GeckoAccessibility.updateAccessibilitySettings(); - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { SharedPreferences prefs = @@ -1995,7 +1994,7 @@ abstract public class GeckoApp // In some way it's sad that Android will trigger StrictMode warnings // here as the whole point is to save to disk while the activity is not // interacting with the user. - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { SharedPreferences prefs = @@ -2017,7 +2016,7 @@ abstract public class GeckoApp @Override public void onRestart() { - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { SharedPreferences prefs = @@ -2213,7 +2212,7 @@ abstract public class GeckoApp if (profileDir != null) { final GeckoApp app = GeckoApp.mAppContext; - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { ProfileMigrator profileMigrator = new ProfileMigrator(app); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 5a3d48f15b4..8f19dcf5fa8 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -12,7 +12,6 @@ import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.PanZoomController; import org.mozilla.gecko.mozglue.GeckoLoader; import org.mozilla.gecko.util.EventDispatcher; -import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.ThreadUtils; @@ -49,7 +48,6 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; -import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; @@ -256,10 +254,6 @@ public class GeckoAppShell } } - public static Handler getHandler() { - return GeckoBackgroundThread.getHandler(); - } - public static void runGecko(String apkPath, String args, String url, String type) { Looper.prepare(); @@ -608,7 +602,7 @@ public class GeckoAppShell public static void createShortcut(final String aTitle, final String aURI, final String aUniqueURI, final Bitmap aIcon, final String aType) { - getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { // the intent to be launched by the shortcut @@ -645,7 +639,7 @@ public class GeckoAppShell } public static void removeShortcut(final String aTitle, final String aURI, final String aUniqueURI, final String aType) { - getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { // the intent to be launched by the shortcut @@ -680,7 +674,7 @@ public class GeckoAppShell // On uninstall, we need to do a couple of things: // 1. nuke the running app process. // 2. nuke the profile that was assigned to that webapp - getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).releaseIndexForApp(uniqueURI); @@ -1117,7 +1111,7 @@ public class GeckoAppShell // Note: the main looper won't work because it may be blocked on the // gecko thread, which is most likely this thread static String getClipboardText() { - getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override @SuppressWarnings("deprecation") public void run() { @@ -1151,7 +1145,7 @@ public class GeckoAppShell } static void setClipboardText(final String text) { - getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override @SuppressWarnings("deprecation") public void run() { @@ -1852,7 +1846,7 @@ public class GeckoAppShell } static void markUriVisited(final String uri) { // invoked from native JNI code - getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { GlobalHistory.getInstance().add(uri); @@ -1861,7 +1855,7 @@ public class GeckoAppShell } static void setUriTitle(final String uri, final String title) { // invoked from native JNI code - getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { GlobalHistory.getInstance().update(uri, title); diff --git a/mobile/android/base/GlobalHistory.java b/mobile/android/base/GlobalHistory.java index 37d1dfb3d77..4e6d96f1a66 100644 --- a/mobile/android/base/GlobalHistory.java +++ b/mobile/android/base/GlobalHistory.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.util.ThreadUtils; import android.database.Cursor; import android.net.Uri; @@ -39,7 +40,7 @@ class GlobalHistory { private boolean mProcessing; // = false // whether or not the runnable is queued/working private GlobalHistory() { - mHandler = GeckoAppShell.getHandler(); + mHandler = ThreadUtils.getBackgroundHandler(); mPendingUris = new LinkedList(); mVisitedCache = new SoftReference>(null); mNotifierRunnable = new Runnable() { diff --git a/mobile/android/base/MemoryMonitor.java b/mobile/android/base/MemoryMonitor.java index 5a6833adb37..37c5669fdf3 100644 --- a/mobile/android/base/MemoryMonitor.java +++ b/mobile/android/base/MemoryMonitor.java @@ -7,6 +7,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserContract; +import org.mozilla.gecko.util.ThreadUtils; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; @@ -105,7 +106,7 @@ class MemoryMonitor extends BroadcastReceiver { if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(intent.getAction())) { Log.d(LOGTAG, "Device storage is low"); mStoragePressure = true; - GeckoAppShell.getHandler().post(new StorageReducer(context)); + ThreadUtils.postToBackgroundThread(new StorageReducer(context)); } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(intent.getAction())) { Log.d(LOGTAG, "Device storage is ok"); mStoragePressure = false; @@ -178,9 +179,9 @@ class MemoryMonitor extends BroadcastReceiver { synchronized void start() { if (mPosted) { // cancel the old one before scheduling a new one - GeckoAppShell.getHandler().removeCallbacks(this); + ThreadUtils.getBackgroundHandler().removeCallbacks(this); } - GeckoAppShell.getHandler().postDelayed(this, DECREMENT_DELAY); + ThreadUtils.getBackgroundHandler().postDelayed(this, DECREMENT_DELAY); mPosted = true; } @@ -193,7 +194,7 @@ class MemoryMonitor extends BroadcastReceiver { } // need to keep decrementing - GeckoAppShell.getHandler().postDelayed(this, DECREMENT_DELAY); + ThreadUtils.getBackgroundHandler().postDelayed(this, DECREMENT_DELAY); } } @@ -207,7 +208,7 @@ class MemoryMonitor extends BroadcastReceiver { public void run() { // this might get run right on startup, if so wait 10 seconds and try again if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { - GeckoAppShell.getHandler().postDelayed(this, 10000); + ThreadUtils.getBackgroundHandler().postDelayed(this, 10000); return; } diff --git a/mobile/android/base/MultiChoicePreference.java b/mobile/android/base/MultiChoicePreference.java index 76e3f45e1f3..dadde055874 100644 --- a/mobile/android/base/MultiChoicePreference.java +++ b/mobile/android/base/MultiChoicePreference.java @@ -5,6 +5,8 @@ package org.mozilla.gecko; +import org.mozilla.gecko.util.ThreadUtils; + import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.Context; @@ -184,7 +186,7 @@ class MultiChoicePreference extends DialogPreference { mPrevValues = mValues.clone(); } - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { for (int i = 0; i < mEntryKeys.length; i++) { @@ -233,7 +235,7 @@ class MultiChoicePreference extends DialogPreference { } mValues = new boolean[entryCount]; - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { for (int i = 0; i < entryCount; i++) { diff --git a/mobile/android/base/ProfileMigrator.java b/mobile/android/base/ProfileMigrator.java index 484c028cccf..1aa905a8707 100644 --- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -14,6 +14,7 @@ import org.mozilla.gecko.sqlite.SQLiteBridge; import org.mozilla.gecko.sqlite.SQLiteBridgeException; import org.mozilla.gecko.sync.setup.SyncAccounts; import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters; +import org.mozilla.gecko.util.ThreadUtils; import android.accounts.Account; import android.content.ContentProviderOperation; @@ -609,7 +610,7 @@ public class ProfileMigrator { final String clientName = mSyncSettingsMap.get("services.sync.client.name"); final String clientGuid = mSyncSettingsMap.get("services.sync.client.GUID"); - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { if (userName == null || syncKey == null || syncPass == null) { @@ -637,7 +638,7 @@ public class ProfileMigrator { } protected void registerAndRequest() { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { requestValues(); @@ -652,7 +653,7 @@ public class ProfileMigrator { @Override protected void onPostExecute(Boolean result) { if (result.booleanValue()) { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { Log.i(LOGTAG, "Sync account already configured, skipping."); @@ -933,7 +934,7 @@ public class ProfileMigrator { // GlobalHistory access communicates with Gecko // and must run on its thread. - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { for (String url : placesHistory) { diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 5f98663dc1d..b3155b7f18d 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -15,6 +15,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.Layer; +import org.mozilla.gecko.util.ThreadUtils; import android.content.ContentResolver; import android.graphics.Bitmap; @@ -163,7 +164,7 @@ public class Tab { } public void updateThumbnail(final Bitmap b) { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { if (b != null) { @@ -321,7 +322,7 @@ public class Tab { } void updateBookmark() { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { final String url = getURL(); @@ -339,7 +340,7 @@ public class Tab { } public void addBookmark() { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { String url = getURL(); @@ -352,7 +353,7 @@ public class Tab { } public void removeBookmark() { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { String url = getURL(); @@ -549,7 +550,7 @@ public class Tab { final String oldURL = getURL(); final Tab tab = this; - GeckoAppShell.getHandler().postDelayed(new Runnable() { + ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() { @Override public void run() { // tab.getURL() may return null diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 40247745d55..4ac1faef2f5 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -6,8 +6,9 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.sync.setup.SyncAccounts; +import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONObject; @@ -106,7 +107,7 @@ public class Tabs implements GeckoEventListener { }; // The listener will run on the background thread (see 2nd argument). - mAccountManager.addOnAccountsUpdatedListener(mAccountListener, GeckoAppShell.getHandler(), false); + mAccountManager.addOnAccountsUpdatedListener(mAccountListener, ThreadUtils.getBackgroundHandler(), false); if (mContentObserver != null) { BrowserDB.registerBookmarkObserver(getContentResolver(), mContentObserver); @@ -449,7 +450,7 @@ public class Tabs implements GeckoEventListener { public void refreshThumbnails() { final ThumbnailHelper helper = ThumbnailHelper.getInstance(); - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { for (final Tab tab : mOrder) { @@ -553,7 +554,7 @@ public class Tabs implements GeckoEventListener { public void persistAllTabs() { final GeckoApp activity = getActivity(); final Iterable tabs = getTabsInOrder(); - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { boolean syncIsSetup = SyncAccounts.syncAccountsExist(activity); diff --git a/mobile/android/base/TabsAccessor.java b/mobile/android/base/TabsAccessor.java index 56bd8575fb9..a09b7b24eef 100644 --- a/mobile/android/base/TabsAccessor.java +++ b/mobile/android/base/TabsAccessor.java @@ -5,6 +5,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserContract; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UiAsyncTask; import org.json.JSONArray; @@ -73,11 +74,11 @@ public final class TabsAccessor { if (listener == null) return; - (new UiAsyncTask>(GeckoAppShell.getHandler()) { + (new UiAsyncTask>(ThreadUtils.getBackgroundHandler()) { @Override protected List doInBackground(Void... unused) { Uri uri = BrowserContract.Tabs.CONTENT_URI; - + if (limit > 0) { uri = uri.buildUpon() .appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit)) diff --git a/mobile/android/base/WebAppAllocator.java b/mobile/android/base/WebAppAllocator.java index e64da544b96..add7d2b0ded 100644 --- a/mobile/android/base/WebAppAllocator.java +++ b/mobile/android/base/WebAppAllocator.java @@ -10,7 +10,7 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import org.mozilla.gecko.gfx.BitmapUtils; -import org.mozilla.gecko.util.GeckoBackgroundThread; +import org.mozilla.gecko.util.ThreadUtils; public class WebAppAllocator { private final String LOGTAG = "GeckoWebAppAllocator"; @@ -70,7 +70,7 @@ public class WebAppAllocator { if (!mPrefs.contains(appKey(i))) { // found unused index i final int foundIndex = i; - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { int color = 0; @@ -118,7 +118,7 @@ public class WebAppAllocator { } public synchronized void releaseIndex(final int index) { - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { mPrefs.edit() diff --git a/mobile/android/base/awesomebar/AllPagesTab.java b/mobile/android/base/awesomebar/AllPagesTab.java index 8c7df57c3a9..c5d291b5cb2 100644 --- a/mobile/android/base/awesomebar/AllPagesTab.java +++ b/mobile/android/base/awesomebar/AllPagesTab.java @@ -177,7 +177,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { * Query for suggestions, but don't show them yet. */ private void primeSuggestions() { - GeckoAppShell.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { mSuggestClient.query(mSearchTerm); @@ -844,7 +844,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { if (urls.size() == 0) return; - (new UiAsyncTask(GeckoAppShell.getHandler()) { + (new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override public Cursor doInBackground(Void... params) { return BrowserDB.getFaviconsForUrls(getContentResolver(), urls); diff --git a/mobile/android/base/awesomebar/HistoryTab.java b/mobile/android/base/awesomebar/HistoryTab.java index 92f0290dcae..d3e14a5a885 100644 --- a/mobile/android/base/awesomebar/HistoryTab.java +++ b/mobile/android/base/awesomebar/HistoryTab.java @@ -356,7 +356,7 @@ public class HistoryTab extends AwesomeBarTab { if (mContentObserver == null) { // Register an observer to update the history tab contents if they change. - mContentObserver = new ContentObserver(GeckoAppShell.getHandler()) { + mContentObserver = new ContentObserver(ThreadUtils.getBackgroundHandler()) { @Override public void onChange(boolean selfChange) { mQueryTask = new HistoryQueryTask(); diff --git a/mobile/android/base/db/BrowserProvider.java.in b/mobile/android/base/db/BrowserProvider.java.in index e68d0df8170..7fa7c5335e9 100644 --- a/mobile/android/base/db/BrowserProvider.java.in +++ b/mobile/android/base/db/BrowserProvider.java.in @@ -38,8 +38,8 @@ import org.mozilla.gecko.db.DBUtils; import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.ProfileMigrator; import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.GeckoJarReader; +import org.mozilla.gecko.util.ThreadUtils; import android.app.SearchManager; import android.content.ContentProvider; @@ -1026,7 +1026,7 @@ public class BrowserProvider extends ContentProvider { } // create icons in a separate thread to avoid blocking about:home on startup - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { try { @@ -1069,7 +1069,7 @@ public class BrowserProvider extends ContentProvider { createBookmark(db, title, url, pos); // create icons in a separate thread to avoid blocking about:home on startup - GeckoBackgroundThread.getHandler().post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { Bitmap icon = getDefaultFaviconFromPath(name); @@ -1993,7 +1993,7 @@ public class BrowserProvider extends ContentProvider { public boolean onCreate() { debug("Creating BrowserProvider"); - GeckoBackgroundThread.post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { // Kick this off early. It is synchronized so that other callers will wait diff --git a/mobile/android/base/db/TabsProvider.java.in b/mobile/android/base/db/TabsProvider.java.in index b7baec0c5a8..2259b6fa9a5 100644 --- a/mobile/android/base/db/TabsProvider.java.in +++ b/mobile/android/base/db/TabsProvider.java.in @@ -15,7 +15,7 @@ import org.mozilla.gecko.db.BrowserContract.Clients; import org.mozilla.gecko.db.BrowserContract.Tabs; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.db.DBUtils; -import org.mozilla.gecko.util.GeckoBackgroundThread; +import org.mozilla.gecko.util.ThreadUtils; import android.content.ContentProvider; import android.content.ContentUris; @@ -289,7 +289,7 @@ public class TabsProvider extends ContentProvider { public boolean onCreate() { debug("Creating TabsProvider"); - GeckoBackgroundThread.post(new Runnable() { + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { // Kick this off early. It is synchronized so that other callers will wait diff --git a/mobile/android/base/util/GeckoBackgroundThread.java b/mobile/android/base/util/GeckoBackgroundThread.java index f08f6cb9605..f7873fe733d 100644 --- a/mobile/android/base/util/GeckoBackgroundThread.java +++ b/mobile/android/base/util/GeckoBackgroundThread.java @@ -9,7 +9,7 @@ import android.os.Looper; import java.util.concurrent.SynchronousQueue; -public final class GeckoBackgroundThread extends Thread { +final class GeckoBackgroundThread extends Thread { private static final String LOOPER_NAME = "GeckoBackgroundThread"; // Guarded by 'this'. @@ -33,7 +33,7 @@ public final class GeckoBackgroundThread extends Thread { } // Get a Handler for a looper thread, or create one if it doesn't yet exist. - public static synchronized Handler getHandler() { + /*package*/ static synchronized Handler getHandler() { if (sHandler == null) { GeckoBackgroundThread lt = new GeckoBackgroundThread(); ThreadUtils.setBackgroundThread(lt); @@ -45,7 +45,7 @@ public final class GeckoBackgroundThread extends Thread { return sHandler; } - public static void post(Runnable runnable) { + /*package*/ static void post(Runnable runnable) { Handler handler = getHandler(); if (handler == null) { throw new IllegalStateException("No handler! Must have been interrupted. Not posting."); diff --git a/mobile/android/base/util/ThreadUtils.java b/mobile/android/base/util/ThreadUtils.java index 29d4a4f8734..8b17069810e 100644 --- a/mobile/android/base/util/ThreadUtils.java +++ b/mobile/android/base/util/ThreadUtils.java @@ -47,6 +47,14 @@ public final class ThreadUtils { return sBackgroundThread; } + public static Handler getBackgroundHandler() { + return GeckoBackgroundThread.getHandler(); + } + + public static void postToBackgroundThread(Runnable runnable) { + GeckoBackgroundThread.post(runnable); + } + public static void assertOnUiThread() { assertOnThread(getUiThread()); } From 8eb77386b2dfc949534bc72fe79af85527f11e00 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 15 Mar 2013 11:52:53 +0100 Subject: [PATCH 075/202] Bug 802130 - Add a ThreadUtils.isOnUiThread helper function. r=mfinkle --- mobile/android/base/GeckoAppShell.java | 4 ++-- mobile/android/base/gfx/JavaPanZoomController.java | 7 ++----- mobile/android/base/util/ThreadUtils.java | 8 ++++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 8f19dcf5fa8..5cbafaaf08e 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -352,7 +352,7 @@ public class GeckoAppShell e.setAckNeeded(true); long time = SystemClock.uptimeMillis(); - boolean isMainThread = (GeckoApp.mAppContext.getMainLooper().getThread() == Thread.currentThread()); + boolean isUiThread = ThreadUtils.isOnUiThread(); synchronized (sEventAckLock) { if (sWaitingForEventAck) { @@ -374,7 +374,7 @@ public class GeckoAppShell } long waited = SystemClock.uptimeMillis() - time; Log.d(LOGTAG, "Gecko event sync taking too long: " + waited + "ms"); - if (isMainThread && waited >= 4000) { + if (isUiThread && waited >= 4000) { Log.w(LOGTAG, "Gecko event sync took too long, aborting!", new Exception()); sWaitingForEventAck = false; break; diff --git a/mobile/android/base/gfx/JavaPanZoomController.java b/mobile/android/base/gfx/JavaPanZoomController.java index 43ec251d5cb..5bffd8b0983 100644 --- a/mobile/android/base/gfx/JavaPanZoomController.java +++ b/mobile/android/base/gfx/JavaPanZoomController.java @@ -5,7 +5,6 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.Tab; @@ -14,6 +13,7 @@ import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.util.EventDispatcher; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONObject; @@ -93,7 +93,6 @@ class JavaPanZoomController private final Axis mY; private final TouchEventHandler mTouchEventHandler; private final EventDispatcher mEventDispatcher; - private Thread mMainThread; /* The timer that handles flings or bounces. */ private Timer mAnimationTimer; @@ -113,7 +112,6 @@ class JavaPanZoomController mY = new AxisY(mSubscroller); mTouchEventHandler = new TouchEventHandler(view.getContext(), view, this); - mMainThread = GeckoApp.mAppContext.getMainLooper().getThread(); checkMainThread(); setState(PanZoomState.NOTHING); @@ -161,9 +159,8 @@ class JavaPanZoomController return mTarget.getViewportMetrics(); } - // for debugging bug 713011; it can be taken out once that is resolved. private void checkMainThread() { - if (mMainThread != Thread.currentThread()) { + if (!ThreadUtils.isOnUiThread()) { // log with full stack trace Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception()); } diff --git a/mobile/android/base/util/ThreadUtils.java b/mobile/android/base/util/ThreadUtils.java index 8b17069810e..5d9aef577ec 100644 --- a/mobile/android/base/util/ThreadUtils.java +++ b/mobile/android/base/util/ThreadUtils.java @@ -79,4 +79,12 @@ public final class ThreadUtils { + " (\"" + currentThread.getName() + ")"); } } + + public static boolean isOnUiThread() { + return isOnThread(getUiThread()); + } + + public static boolean isOnThread(Thread thread) { + return (Thread.currentThread().getId() == thread.getId()); + } } From 26b31ecf0ab98cbc82a00330c31647a54bbe5749 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 15 Mar 2013 02:29:02 -0700 Subject: [PATCH 076/202] Bug 840282 - OdinMonkey (sr=dmandelin) --HG-- extra : rebase_source : 4a3869dca32755abb58bbd3d9a06599e61b397f2 --- dom/base/nsJSEnvironment.cpp | 8 + dom/workers/RuntimeService.cpp | 5 + js/public/MemoryMetrics.h | 1 + js/public/Utility.h | 7 + js/public/Vector.h | 8 +- js/src/Makefile.in | 5 + js/src/assembler/assembler/X86Assembler.h | 252 +- js/src/assembler/jit/ExecutableAllocator.cpp | 6 +- js/src/assembler/jit/ExecutableAllocator.h | 8 +- js/src/builtin/TestingFunctions.cpp | 5 + js/src/frontend/BytecodeCompiler.cpp | 8 + js/src/frontend/BytecodeEmitter.cpp | 19 + js/src/frontend/FoldConstants.cpp | 10 + js/src/frontend/FullParseHandler.h | 4 +- js/src/frontend/ParseNode-inl.h | 1 + js/src/frontend/ParseNode.cpp | 10 +- js/src/frontend/ParseNode.h | 3 +- js/src/frontend/Parser-inl.h | 2 + js/src/frontend/Parser.cpp | 37 +- js/src/frontend/Parser.h | 4 + js/src/frontend/SharedContext-inl.h | 7 - js/src/frontend/SharedContext.h | 15 + js/src/frontend/SyntaxParseHandler.h | 3 +- js/src/frontend/TokenStream.cpp | 9 + js/src/frontend/TokenStream.h | 3 + js/src/gc/Barrier.h | 1 + js/src/gc/Marking.cpp | 1 + js/src/gc/Marking.h | 1 + js/src/ion/AsmJS.cpp | 5007 +++++++++++++++++ js/src/ion/AsmJS.h | 105 + js/src/ion/AsmJSLink.cpp | 403 ++ js/src/ion/AsmJSModule.h | 549 ++ js/src/ion/AsmJSSignalHandlers.cpp | 563 ++ js/src/ion/CodeGenerator.cpp | 130 +- js/src/ion/CodeGenerator.h | 9 +- js/src/ion/CompileInfo.h | 39 +- js/src/ion/EffectiveAddressAnalysis.cpp | 119 + js/src/ion/EffectiveAddressAnalysis.h | 32 + js/src/ion/Ion.cpp | 36 +- js/src/ion/Ion.h | 8 +- js/src/ion/IonAllocPolicy.h | 3 + js/src/ion/IonAnalysis.cpp | 1 + js/src/ion/IonBuilder.cpp | 5 + js/src/ion/IonMacroAssembler.cpp | 35 +- js/src/ion/IonMacroAssembler.h | 76 +- js/src/ion/IonTypes.h | 2 +- js/src/ion/LIR-Common.h | 200 +- js/src/ion/LIR.cpp | 5 +- js/src/ion/LIR.h | 29 +- js/src/ion/LOpcodes.h | 17 +- js/src/ion/Lowering.cpp | 132 +- js/src/ion/Lowering.h | 11 + js/src/ion/MIR.cpp | 168 +- js/src/ion/MIR.h | 551 +- js/src/ion/MIRGenerator.h | 55 + js/src/ion/MIRGraph.cpp | 37 +- js/src/ion/MIRGraph.h | 5 +- js/src/ion/MOpcodes.h | 17 + js/src/ion/MoveResolver.h | 3 + js/src/ion/ParallelArrayAnalysis.cpp | 17 + js/src/ion/RangeAnalysis.cpp | 6 +- js/src/ion/RegisterAllocator.h | 4 + js/src/ion/RegisterSets.h | 86 + js/src/ion/Safepoints.cpp | 2 +- js/src/ion/StupidAllocator.cpp | 2 +- js/src/ion/TypeOracle.h | 4 +- js/src/ion/arm/Assembler-arm.cpp | 9 +- js/src/ion/arm/Assembler-arm.h | 6 +- js/src/ion/arm/CodeGenerator-arm.cpp | 25 +- js/src/ion/arm/CodeGenerator-arm.h | 5 +- js/src/ion/arm/LIR-arm.h | 11 + js/src/ion/arm/LOpcodes-arm.h | 3 +- js/src/ion/arm/MacroAssembler-arm.h | 3 + js/src/ion/shared/Assembler-shared.h | 27 +- js/src/ion/shared/Assembler-x86-shared.cpp | 4 +- js/src/ion/shared/Assembler-x86-shared.h | 34 +- js/src/ion/shared/CodeGenerator-shared-inl.h | 5 + js/src/ion/shared/CodeGenerator-shared.cpp | 40 +- js/src/ion/shared/CodeGenerator-shared.h | 12 +- .../ion/shared/CodeGenerator-x86-shared.cpp | 66 +- js/src/ion/shared/CodeGenerator-x86-shared.h | 5 +- js/src/ion/shared/LIR-x86-shared.h | 18 + js/src/ion/shared/Lowering-shared-inl.h | 10 +- js/src/ion/shared/Lowering-shared.h | 4 +- js/src/ion/shared/Lowering-x86-shared.cpp | 28 + js/src/ion/shared/Lowering-x86-shared.h | 3 + js/src/ion/shared/MacroAssembler-x86-shared.h | 16 + js/src/ion/x64/Assembler-x64.cpp | 68 + js/src/ion/x64/Assembler-x64.h | 63 + js/src/ion/x64/CodeGenerator-x64.cpp | 151 +- js/src/ion/x64/CodeGenerator-x64.h | 11 +- js/src/ion/x64/LIR-x64.h | 30 + js/src/ion/x64/LOpcodes-x64.h | 5 +- js/src/ion/x64/Lowering-x64.cpp | 37 + js/src/ion/x64/Lowering-x64.h | 3 + js/src/ion/x64/MacroAssembler-x64.h | 26 + js/src/ion/x86/Architecture-x86.h | 3 + js/src/ion/x86/Assembler-x86.cpp | 27 + js/src/ion/x86/Assembler-x86.h | 122 +- js/src/ion/x86/CodeGenerator-x86.cpp | 193 +- js/src/ion/x86/CodeGenerator-x86.h | 15 +- js/src/ion/x86/LIR-x86.h | 30 + js/src/ion/x86/LOpcodes-x86.h | 5 +- js/src/ion/x86/Lowering-x86.cpp | 43 + js/src/ion/x86/Lowering-x86.h | 3 + js/src/ion/x86/MacroAssembler-x86.h | 23 + js/src/jit-test/lib/asm.js | 147 + js/src/jit-test/tests/asm.js/testBasic.js | 98 + js/src/jit-test/tests/asm.js/testCall.js | 49 + .../tests/asm.js/testCompoundPlusMinus.js | 14 + .../jit-test/tests/asm.js/testControlFlow.js | 165 + .../tests/asm.js/testDebugModeDisables.js | 5 + .../jit-test/tests/asm.js/testExpressions.js | 279 + js/src/jit-test/tests/asm.js/testFFI.js | 88 + .../tests/asm.js/testFastHeapAccess.js | 68 + .../tests/asm.js/testFloatingPoint.js | 126 + .../jit-test/tests/asm.js/testFunctionPtr.js | 49 + js/src/jit-test/tests/asm.js/testGlobals.js | 57 + .../jit-test/tests/asm.js/testHeapAccess.js | 141 + js/src/jit-test/tests/asm.js/testLiterals.js | 38 + js/src/jit-test/tests/asm.js/testMathLib.js | 88 + js/src/jit-test/tests/asm.js/testTimeout1.js | 8 + js/src/jit-test/tests/asm.js/testTimeout2.js | 8 + js/src/jit-test/tests/asm.js/testTimeout3.js | 8 + js/src/jit-test/tests/asm.js/testTimeout4.js | 8 + .../jit-test/tests/asm.js/testX86ByteStore.js | 77 + js/src/jit-test/tests/asm.js/testZOOB.js | 96 + .../jit-test/tests/auto-regress/bug759312.js | 60 - js/src/js.msg | 4 + js/src/jsanalyze.cpp | 4 + js/src/jsapi.cpp | 33 +- js/src/jsapi.h | 3 +- js/src/jscntxt.cpp | 16 +- js/src/jscntxt.h | 50 + js/src/jsfun.h | 1 + js/src/jsinfer.cpp | 1 + js/src/jsinterp.cpp | 40 +- js/src/jsmath.cpp | 13 +- js/src/jsmath.h | 23 + js/src/jsobjinlines.h | 24 +- js/src/jsopcode.tbl | 4 +- js/src/jsscript.cpp | 3 + js/src/jsscript.h | 8 + js/src/jstypedarray.cpp | 176 +- js/src/jstypedarray.h | 34 +- js/src/jstypedarrayinlines.h | 6 + js/src/jswin.h | 1 + js/src/methodjit/FastOps.cpp | 8 + js/src/shell/js.cpp | 8 + js/src/vm/CommonPropertyNames.h | 1 + js/src/vm/ObjectImpl.h | 9 +- js/src/vm/Xdr.h | 2 +- js/xpconnect/shell/xpcshell.cpp | 1 + js/xpconnect/src/XPCJSRuntime.cpp | 4 + modules/libpref/src/init/all.js | 5 + 155 files changed, 11942 insertions(+), 325 deletions(-) create mode 100644 js/src/ion/AsmJS.cpp create mode 100644 js/src/ion/AsmJS.h create mode 100644 js/src/ion/AsmJSLink.cpp create mode 100644 js/src/ion/AsmJSModule.h create mode 100644 js/src/ion/AsmJSSignalHandlers.cpp create mode 100644 js/src/ion/EffectiveAddressAnalysis.cpp create mode 100644 js/src/ion/EffectiveAddressAnalysis.h create mode 100644 js/src/jit-test/lib/asm.js create mode 100644 js/src/jit-test/tests/asm.js/testBasic.js create mode 100644 js/src/jit-test/tests/asm.js/testCall.js create mode 100644 js/src/jit-test/tests/asm.js/testCompoundPlusMinus.js create mode 100644 js/src/jit-test/tests/asm.js/testControlFlow.js create mode 100644 js/src/jit-test/tests/asm.js/testDebugModeDisables.js create mode 100644 js/src/jit-test/tests/asm.js/testExpressions.js create mode 100644 js/src/jit-test/tests/asm.js/testFFI.js create mode 100644 js/src/jit-test/tests/asm.js/testFastHeapAccess.js create mode 100644 js/src/jit-test/tests/asm.js/testFloatingPoint.js create mode 100644 js/src/jit-test/tests/asm.js/testFunctionPtr.js create mode 100644 js/src/jit-test/tests/asm.js/testGlobals.js create mode 100644 js/src/jit-test/tests/asm.js/testHeapAccess.js create mode 100644 js/src/jit-test/tests/asm.js/testLiterals.js create mode 100644 js/src/jit-test/tests/asm.js/testMathLib.js create mode 100644 js/src/jit-test/tests/asm.js/testTimeout1.js create mode 100644 js/src/jit-test/tests/asm.js/testTimeout2.js create mode 100644 js/src/jit-test/tests/asm.js/testTimeout3.js create mode 100644 js/src/jit-test/tests/asm.js/testTimeout4.js create mode 100644 js/src/jit-test/tests/asm.js/testX86ByteStore.js create mode 100644 js/src/jit-test/tests/asm.js/testZOOB.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug759312.js diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 827bdea46d9..c60a363e63b 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -974,6 +974,7 @@ static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify"; static const char js_disable_explicit_compartment_gc[] = JS_OPTIONS_DOT_STR "mem.disable_explicit_compartment_gc"; static const char js_ion_content_str[] = JS_OPTIONS_DOT_STR "ion.content"; +static const char js_asmjs_content_str[] = JS_OPTIONS_DOT_STR "experimental_asmjs"; static const char js_ion_parallel_compilation_str[] = JS_OPTIONS_DOT_STR "ion.parallel_compilation"; int @@ -1014,6 +1015,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) bool useTypeInference = !chromeWindow && contentWindow && Preferences::GetBool(js_typeinfer_str); bool useHardening = Preferences::GetBool(js_jit_hardening_str); bool useIon = Preferences::GetBool(js_ion_content_str); + bool useAsmJS = Preferences::GetBool(js_asmjs_content_str); bool parallelIonCompilation = Preferences::GetBool(js_ion_parallel_compilation_str); nsCOMPtr xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID); if (xr) { @@ -1026,6 +1028,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) useMethodJITAlways = true; useHardening = false; useIon = false; + useAsmJS = false; } } @@ -1054,6 +1057,11 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) else newDefaultJSOptions &= ~JSOPTION_ION; + if (useAsmJS) + newDefaultJSOptions |= JSOPTION_ASMJS; + else + newDefaultJSOptions &= ~JSOPTION_ASMJS; + #ifdef DEBUG // In debug builds, warnings are enabled in chrome context if // javascript.options.strict.debug is true diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 89125f3c53d..53cd3719f91 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -154,6 +154,7 @@ enum { PREF_jit_hardening, PREF_mem_max, PREF_ion, + PREF_asmjs, PREF_mem_gc_allocation_threshold_mb, #ifdef JS_GC_ZEAL @@ -174,6 +175,7 @@ const char* gPrefsToWatch[] = { JS_OPTIONS_DOT_STR "jit_hardening", JS_OPTIONS_DOT_STR "mem.max", JS_OPTIONS_DOT_STR "ion.content", + JS_OPTIONS_DOT_STR "experimental_asmjs", "dom.workers.mem.gc_allocation_threshold_mb" #ifdef JS_GC_ZEAL @@ -228,6 +230,9 @@ PrefCallback(const char* aPrefName, void* aClosure) if (Preferences::GetBool(gPrefsToWatch[PREF_ion])) { newOptions |= JSOPTION_ION; } + if (Preferences::GetBool(gPrefsToWatch[PREF_asmjs])) { + newOptions |= JSOPTION_ASMJS; + } RuntimeService::SetDefaultJSContextOptions(newOptions); rts->UpdateAllWorkerJSContextOptions(); diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 625ed408f4f..d0a91255d39 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -129,6 +129,7 @@ struct RuntimeSizes size_t temporary; size_t jaegerCode; size_t ionCode; + size_t asmJSCode; size_t regexpCode; size_t unusedCode; size_t regexpData; diff --git a/js/public/Utility.h b/js/public/Utility.h index 0020be6b460..79fb25fa18c 100644 --- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -563,6 +563,13 @@ struct ScopedDeletePtrTraits : public ScopedFreePtrTraits }; SCOPED_TEMPLATE(ScopedJSDeletePtr, ScopedDeletePtrTraits) +template +struct ScopedReleasePtrTraits : public ScopedFreePtrTraits +{ + static void release(T *ptr) { if (ptr) ptr->release(); } +}; +SCOPED_TEMPLATE(ScopedReleasePtr, ScopedReleasePtrTraits) + } /* namespace js */ namespace js { diff --git a/js/public/Vector.h b/js/public/Vector.h index 66f16b78d1a..e9a5cce1db3 100644 --- a/js/public/Vector.h +++ b/js/public/Vector.h @@ -529,7 +529,7 @@ Vector::Vector(AllocPolicy ap) : AllocPolicy(ap), mBegin((T *)storage.addr()), mLength(0), mCapacity(sInlineCapacity) #ifdef DEBUG - , mReserved(0), entered(false) + , mReserved(sInlineCapacity), entered(false) #endif {} @@ -566,7 +566,7 @@ Vector::Vector(MoveRef rhs) rhs->mCapacity = sInlineCapacity; rhs->mLength = 0; #ifdef DEBUG - rhs->mReserved = 0; + rhs->mReserved = sInlineCapacity; #endif } } @@ -806,7 +806,7 @@ Vector::clearAndFree() mBegin = (T *)storage.addr(); mCapacity = sInlineCapacity; #ifdef DEBUG - mReserved = 0; + mReserved = sInlineCapacity; #endif } @@ -996,7 +996,7 @@ Vector::extractRawBuffer() mLength = 0; mCapacity = sInlineCapacity; #ifdef DEBUG - mReserved = 0; + mReserved = sInlineCapacity; #endif } return ret; diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 13f15ef130b..f1ce70fa8e1 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -46,6 +46,7 @@ VPATH += \ $(srcdir)/ds \ $(srcdir)/frontend \ $(srcdir)/gc \ + $(srcdir)/ion \ $(srcdir)/vm \ $(NULL) @@ -143,6 +144,9 @@ CPPSRCS = \ Unicode.cpp \ Xdr.cpp \ Module.cpp \ + AsmJS.cpp \ + AsmJSLink.cpp \ + AsmJSSignalHandlers.cpp \ $(NULL) # Changes to internal header files, used externally, massively slow down @@ -299,6 +303,7 @@ CPPSRCS += MIR.cpp \ AliasAnalysis.cpp \ ParallelArrayAnalysis.cpp \ UnreachableCodeElimination.cpp \ + EffectiveAddressAnalysis.cpp \ $(NULL) endif #ENABLE_ION ifeq (86, $(findstring 86,$(TARGET_CPU))) diff --git a/js/src/assembler/assembler/X86Assembler.h b/js/src/assembler/assembler/X86Assembler.h index 326baf5da6f..2293098864f 100644 --- a/js/src/assembler/assembler/X86Assembler.h +++ b/js/src/assembler/assembler/X86Assembler.h @@ -227,6 +227,8 @@ private: OP_LEA = 0x8D, OP_GROUP1A_Ev = 0x8F, OP_NOP = 0x90, + OP_PUSHFLAGS = 0x9C, + OP_POPFLAGS = 0x9D, OP_CDQ = 0x99, OP_MOV_EAXOv = 0xA1, OP_MOV_OvEAX = 0xA3, @@ -325,6 +327,7 @@ private: GROUP3_OP_TEST = 0, GROUP3_OP_NOT = 2, GROUP3_OP_NEG = 3, + GROUP3_OP_DIV = 6, GROUP3_OP_IDIV = 7, GROUP5_OP_CALLN = 2, @@ -438,6 +441,18 @@ public: m_formatter.oneByteOp(OP_GROUP1A_Ev, GROUP1A_OP_POP, base, offset); } + void push_flags() + { + spew("push flags register"); + m_formatter.oneByteOp(OP_PUSHFLAGS); + } + + void pop_flags() + { + spew("pop flags register"); + m_formatter.oneByteOp(OP_POPFLAGS); + } + // Arithmetic operations: #if !WTF_CPU_X86_64 @@ -1024,11 +1039,18 @@ public: m_formatter.immediate32(value); } - void idivl_r(RegisterID dst) + void idivl_r(RegisterID divisor) { spew("idivl %s", - nameIReg(4, dst)); - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, dst); + nameIReg(4, divisor)); + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, divisor); + } + + void divl_r(RegisterID divisor) + { + spew("div %s", + nameIReg(4, divisor)); + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_DIV, divisor); } // Comparisons: @@ -1384,6 +1406,14 @@ public: m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset); } + void movw_rm_disp32(RegisterID src, int offset, RegisterID base) + { + spew("movw %s, %s0x%x(%s)", + nameIReg(2,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base)); + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset); + } + void movl_rm(RegisterID src, int offset, RegisterID base) { spew("movl %s, %s0x%x(%s)", @@ -1411,7 +1441,7 @@ public: nameIReg(4, src), offset, nameIReg(base), nameIReg(index), scale); m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset); } - + void movl_mEAX(void* addr) { FIXME_INSN_PRINTING; @@ -1436,6 +1466,15 @@ public: m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset); } +#if WTF_CPU_X86 + void movl_mr(void* base, RegisterID index, int scale, RegisterID dst) + { + spew("movl %d(%s,%d), %s", + int(base), nameIReg(index), scale, nameIReg(dst)); + m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, index, scale, int(base)); + } +#endif + void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { spew("movl %d(%s,%s,%d), %s", @@ -1617,7 +1656,29 @@ public: m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src); } - + JmpSrc movl_ripr(RegisterID dst) + { + spew("movl \?(%%rip), %s", + nameIReg(dst)); + m_formatter.oneByteRipOp(OP_MOV_GvEv, (RegisterID)dst, 0); + return JmpSrc(m_formatter.size()); + } + + JmpSrc movl_rrip(RegisterID src) + { + spew("movl %s, \?(%%rip)", + nameIReg(src)); + m_formatter.oneByteRipOp(OP_MOV_EvGv, (RegisterID)src, 0); + return JmpSrc(m_formatter.size()); + } + + JmpSrc movq_ripr(RegisterID dst) + { + spew("movl \?(%%rip), %s", + nameIReg(dst)); + m_formatter.oneByteRipOp64(OP_MOV_GvEv, dst, 0); + return JmpSrc(m_formatter.size()); + } #else void movl_rm(RegisterID src, void* addr) { @@ -1654,6 +1715,13 @@ public: m_formatter.oneByteOp8(OP_MOV_EbGv, src, base, offset); } + void movb_rm_disp32(RegisterID src, int offset, RegisterID base) + { + spew("movb %s, %s0x%x(%s)", + nameIReg(1, src), PRETTY_PRINT_OFFSET(offset), nameIReg(base)); + m_formatter.oneByteOp8_disp32(OP_MOV_EbGv, src, base, offset); + } + void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) { spew("movb %s, %d(%s,%s,%d)", @@ -1668,6 +1736,13 @@ public: m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset); } + void movzbl_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + spew("movzbl %s0x%x(%s), %s", + PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst)); + m_formatter.twoByteOp_disp32(OP2_MOVZX_GvEb, dst, base, offset); + } + void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { spew("movzbl %d(%s,%s,%d), %s", @@ -1682,6 +1757,13 @@ public: m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset); } + void movxbl_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + spew("movxbl %s0x%x(%s), %s", + PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst)); + m_formatter.twoByteOp_disp32(OP2_MOVSX_GvEb, dst, base, offset); + } + void movxbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { spew("movxbl %d(%s,%s,%d), %s", @@ -1696,6 +1778,13 @@ public: m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset); } + void movzwl_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + spew("movzwl %s0x%x(%s), %s", + PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst)); + m_formatter.twoByteOp_disp32(OP2_MOVZX_GvEw, dst, base, offset); + } + void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { spew("movzwl %d(%s,%s,%d), %s", @@ -1710,6 +1799,13 @@ public: m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset); } + void movxwl_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + spew("movxwl %s0x%x(%s), %s", + PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst)); + m_formatter.twoByteOp_disp32(OP2_MOVSX_GvEw, dst, base, offset); + } + void movxwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { spew("movxwl %d(%s,%s,%d), %s", @@ -1747,6 +1843,14 @@ public: PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(8,dst)); m_formatter.oneByteOp64(OP_LEA, dst, base, offset); } + + JmpSrc leaq_rip(RegisterID dst) + { + spew("leaq \?(%%rip), %s", + nameIReg(dst)); + m_formatter.oneByteRipOp64(OP_LEA, dst, 0); + return JmpSrc(m_formatter.size()); + } #endif // Flow control: @@ -2141,6 +2245,14 @@ public: m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); } + void movsd_rm_disp32(XMMRegisterID src, int offset, RegisterID base) + { + spew("movsd %s, %s0x%x(%s)", + nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base)); + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); + } + void movss_rm(XMMRegisterID src, int offset, RegisterID base) { spew("movss %s, %s0x%x(%s)", @@ -2149,6 +2261,14 @@ public: m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); } + void movss_rm_disp32(XMMRegisterID src, int offset, RegisterID base) + { + spew("movss %s, %s0x%x(%s)", + nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base)); + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); + } + void movss_mr(int offset, RegisterID base, XMMRegisterID dst) { spew("movss %s0x%x(%s), %s", @@ -2157,6 +2277,14 @@ public: m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); } + void movss_mr_disp32(int offset, RegisterID base, XMMRegisterID dst) + { + spew("movss %s0x%x(%s), %s", + PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst)); + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); + } + void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) { spew("movsd %s, %d(%s,%s,%d)", @@ -2189,6 +2317,14 @@ public: m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); } + void movsd_mr_disp32(int offset, RegisterID base, XMMRegisterID dst) + { + spew("movsd %s0x%x(%s), %s", + PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst)); + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); + } + void movsd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) { spew("movsd %d(%s,%s,%d), %s", @@ -2213,6 +2349,31 @@ public: m_formatter.prefix(PRE_SSE_F2); m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address); } + + void movsd_rm(XMMRegisterID src, const void* address) + { + spew("movsd %s, %p", + nameFPReg(src), address); + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address); + } +#else + JmpSrc movsd_ripr(XMMRegisterID dst) + { + spew("movsd \?(%%rip), %s", + nameFPReg(dst)); + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteRipOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, 0); + return JmpSrc(m_formatter.size()); + } + JmpSrc movsd_rrip(XMMRegisterID src) + { + spew("movsd %s, \?(%%rip)", + nameFPReg(src)); + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteRipOp(OP2_MOVSD_WsdVsd, (RegisterID)src, 0); + return JmpSrc(m_formatter.size()); + } #endif void movdqa_rm(XMMRegisterID src, int offset, RegisterID base) @@ -2458,6 +2619,11 @@ public: m_formatter.doubleConstant(d); } + void int64Constant(int64_t i) + { + m_formatter.int64Constant(i); + } + // Linking & patching: // // 'link' and 'patch' methods are for use on unprotected code - such as the code @@ -2754,6 +2920,14 @@ private: memoryModRM(reg, base, index, scale, offset); } + void oneByteOp_disp32(OneByteOpcodeID opcode, int reg, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, index, 0); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, index, scale, offset); + } + #if !WTF_CPU_X86_64 void oneByteOp(OneByteOpcodeID opcode, int reg, void* address) { @@ -2765,6 +2939,26 @@ private: void oneByteRipOp(OneByteOpcodeID opcode, int reg, int ripOffset) { m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, 0); + m_buffer.putByteUnchecked(opcode); + putModRm(ModRmMemoryNoDisp, reg, noBase); + m_buffer.putIntUnchecked(ripOffset); + } + + void oneByteRipOp64(OneByteOpcodeID opcode, int reg, int ripOffset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, 0); + m_buffer.putByteUnchecked(opcode); + putModRm(ModRmMemoryNoDisp, reg, noBase); + m_buffer.putIntUnchecked(ripOffset); + } + + void twoByteRipOp(TwoByteOpcodeID opcode, int reg, int ripOffset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, 0); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); putModRm(ModRmMemoryNoDisp, reg, noBase); m_buffer.putIntUnchecked(ripOffset); @@ -2796,6 +2990,15 @@ private: memoryModRM(reg, base, offset); } + void twoByteOp_disp32(TwoByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) { m_buffer.ensureSpace(maxInstructionSize); @@ -2945,6 +3148,17 @@ private: memoryModRM(reg, base, offset); } + void oneByteOp8_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { +#if !WTF_CPU_X86_64 + ASSERT(!byteRegRequiresRex(reg)); +#endif + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg), reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) { #if !WTF_CPU_X86_64 @@ -3028,6 +3242,12 @@ private: m_buffer.putInt64Unchecked(u.u64); } + void int64Constant(int64_t i) + { + m_buffer.ensureSpace(sizeof(int64_t)); + m_buffer.putInt64Unchecked(i); + } + // Administrative methods: size_t size() const { return m_buffer.size(); } @@ -3194,6 +3414,28 @@ private: } } + void memoryModRM_disp32(int reg, RegisterID index, int scale, int offset) + { + ASSERT(index != noIndex); + + // NB: the base-less memoryModRM overloads generate different code + // then the base-full memoryModRM overloads in the base == noBase + // case. The base-less overloads assume that the desired effective + // address is: + // + // reg := [scaled index] + disp32 + // + // which means the mod needs to be ModRmMemoryNoDisp. The base-full + // overloads pass ModRmMemoryDisp32 in all cases and thus, when + // base == noBase (== ebp), the effective address is: + // + // reg := [scaled index] + disp32 + [ebp] + // + // See Intel developer manual, Vol 2, 2.1.5, Table 2-3. + putModRmSib(ModRmMemoryNoDisp, reg, noBase, index, scale); + m_buffer.putIntUnchecked(offset); + } + #if !WTF_CPU_X86_64 void memoryModRM(int reg, const void* address) { diff --git a/js/src/assembler/jit/ExecutableAllocator.cpp b/js/src/assembler/jit/ExecutableAllocator.cpp index 8db1f4aee07..c2355032ece 100644 --- a/js/src/assembler/jit/ExecutableAllocator.cpp +++ b/js/src/assembler/jit/ExecutableAllocator.cpp @@ -40,10 +40,11 @@ ExecutablePool::~ExecutablePool() } void -ExecutableAllocator::sizeOfCode(size_t *jaeger, size_t *ion, size_t *regexp, size_t *unused) const +ExecutableAllocator::sizeOfCode(size_t *jaeger, size_t *ion, size_t *asmJS, size_t *regexp, size_t *unused) const { *jaeger = 0; *ion = 0; + *asmJS = 0; *regexp = 0; *unused = 0; @@ -52,9 +53,10 @@ ExecutableAllocator::sizeOfCode(size_t *jaeger, size_t *ion, size_t *regexp, siz ExecutablePool* pool = r.front(); *jaeger += pool->m_jaegerCodeBytes; *ion += pool->m_ionCodeBytes; + *asmJS += pool->m_asmJSCodeBytes; *regexp += pool->m_regexpCodeBytes; *unused += pool->m_allocation.size - pool->m_jaegerCodeBytes - pool->m_ionCodeBytes - - pool->m_regexpCodeBytes; + - pool->m_asmJSCodeBytes - pool->m_regexpCodeBytes; } } } diff --git a/js/src/assembler/jit/ExecutableAllocator.h b/js/src/assembler/jit/ExecutableAllocator.h index 20015a255b8..bc8b4951a84 100644 --- a/js/src/assembler/jit/ExecutableAllocator.h +++ b/js/src/assembler/jit/ExecutableAllocator.h @@ -82,7 +82,7 @@ namespace JSC { class ExecutableAllocator; - enum CodeKind { JAEGER_CODE, ION_CODE, REGEXP_CODE }; + enum CodeKind { JAEGER_CODE, ION_CODE, REGEXP_CODE, ASMJS_CODE }; // These are reference-counted. A new one starts with a count of 1. class ExecutablePool { @@ -108,6 +108,7 @@ private: // Number of bytes currently used for Method and Regexp JIT code. size_t m_jaegerCodeBytes; size_t m_ionCodeBytes; + size_t m_asmJSCodeBytes; size_t m_regexpCodeBytes; public: @@ -129,7 +130,7 @@ public: ExecutablePool(ExecutableAllocator* allocator, Allocation a) : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a), - m_refCount(1), m_jaegerCodeBytes(0), m_ionCodeBytes(0), m_regexpCodeBytes(0), + m_refCount(1), m_jaegerCodeBytes(0), m_ionCodeBytes(0), m_asmJSCodeBytes(0), m_regexpCodeBytes(0), m_destroy(false), m_gcNumber(0) { } @@ -154,6 +155,7 @@ private: switch (kind) { case JAEGER_CODE: m_jaegerCodeBytes += n; break; case ION_CODE: m_ionCodeBytes += n; break; + case ASMJS_CODE: m_asmJSCodeBytes += n; break; case REGEXP_CODE: m_regexpCodeBytes += n; break; default: JS_NOT_REACHED("bad code kind"); break; } @@ -253,7 +255,7 @@ public: m_pools.remove(m_pools.lookup(pool)); // this asserts if |pool| is not in m_pools } - void sizeOfCode(size_t *jaeger, size_t *ion, size_t *regexp, size_t *unused) const; + void sizeOfCode(size_t *jaeger, size_t *ion, size_t *asmJS, size_t *regexp, size_t *unused) const; void setDestroyCallback(DestroyCallback destroyCallback) { this->destroyCallback = destroyCallback; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index ce9f52d11dd..374ad0c6203 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1040,6 +1040,11 @@ static JSFunctionSpecWithHelp TestingFunctions[] = { " inferred name based on where the function was defined. This can be\n" " different from the 'name' property on the function."), + JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0, +"isAsmJSCompilationAvailable", +" Returns whether asm.js compilation is currently available or whether it is disabled\n" +" (e.g., by the debugger)."), + JS_FN_HELP("inParallelSection", testingFunc_inParallelSection, 0, 0, "inParallelSection()", " True if this code is executing within a parallel section."), diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index feed1070fda..69d9562154c 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -12,6 +12,7 @@ #include "frontend/BytecodeEmitter.h" #include "frontend/FoldConstants.h" #include "frontend/NameFunctions.h" +#include "ion/AsmJS.h" #include "vm/GlobalObject.h" #include "jsinferinlines.h" @@ -406,6 +407,13 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions pn = fn->pn_body; } + /* + * Do asm.js compilation once the parse tree has been fully assembled but + * before emitting since we need to know whether to emit JSOP_LINKASMJS. + */ + if (fn->pn_funbox->useAsm && !CompileAsmJS(cx, parser.tokenStream, fn, script)) + return false; + if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 98080160c28..175bce15362 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -34,6 +34,7 @@ #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" +#include "ion/AsmJS.h" #include "vm/Debugger.h" #include "vm/RegExpObject.h" #include "vm/Shape.h" @@ -2499,6 +2500,17 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod bce->switchToMain(); } + if (bce->script->asmJS) { + /* asm.js means no funny stuff. */ + JS_ASSERT(!funbox->argumentsHasLocalBinding()); + JS_ASSERT(!funbox->isGenerator()); + + bce->switchToProlog(); + if (Emit1(cx, bce, JSOP_LINKASMJS) < 0) + return false; + bce->switchToMain(); + } + if (!EmitTree(cx, bce, body)) return false; @@ -4380,6 +4392,13 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) script->bindings = funbox->bindings; + // Do asm.js compilation at the beginning of emitting to avoid + // compiling twice when JS_BufferIsCompilableUnit and to know whether + // to emit JSOP_LINKASMJS. Don't fold constants as this will + // misrepresent the source JS as written to the type checker. + if (funbox->useAsm && !CompileAsmJS(cx, *bce->tokenStream(), pn, script)) + return false; + BytecodeEmitter bce2(bce, bce->parser, funbox, script, bce->evalCaller, bce->hasGlobalScope, pn->pn_pos.begin.lineno, bce->selfHostingMode); if (!bce2.init()) diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 2d143c7d3ea..b78d821f897 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -16,6 +16,7 @@ #include "jsatominlines.h" +#include "frontend/Parser-inl.h" #include "vm/String-inl.h" using namespace js; @@ -254,8 +255,17 @@ FoldConstants(JSContext *cx, ParseNode **pnp, JS_CHECK_RECURSION(cx, return false); + // Don't fold constants if the code has requested "use asm" as + // constant-folding will misrepresent the source text for the purpose + // of type checking. (Also guard against entering a function containing + // "use asm", see PN_FUNC case below.) + if (parser->pc->useAsmOrInsideUseAsm()) + return true; + switch (pn->getArity()) { case PN_CODE: + if (pn->pn_funbox->useAsmOrInsideUseAsm()) + return true; if (pn->getKind() == PNK_MODULE) { if (!FoldConstants(cx, &pn->pn_body, parser)) return false; diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 807b6337fcb..f8e560208ee 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -120,8 +120,8 @@ class FullParseHandler return new_(kind, op, pos, left, right); } ParseNode *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right, - JSOp op = JSOP_NOP) { - return ParseNode::newBinaryOrAppend(kind, op, left, right, this, foldConstants); + ParseContext *pc, JSOp op = JSOP_NOP) { + return ParseNode::newBinaryOrAppend(kind, op, left, right, this, pc, foldConstants); } void setBinaryRHS(ParseNode *pn, ParseNode *rhs) { JS_ASSERT(pn->isArity(PN_BINARY)); diff --git a/js/src/frontend/ParseNode-inl.h b/js/src/frontend/ParseNode-inl.h index e31ec15f900..d35c61409ec 100644 --- a/js/src/frontend/ParseNode-inl.h +++ b/js/src/frontend/ParseNode-inl.h @@ -7,6 +7,7 @@ #ifndef ParseNode_inl_h__ #define ParseNode_inl_h__ +#include "frontend/Parser.h" #include "frontend/ParseNode.h" namespace js { diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index ae91d22c128..efafe29caf6 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -304,11 +304,19 @@ ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right ParseNode * ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, - FullParseHandler *handler, bool foldConstants) + FullParseHandler *handler, ParseContext *pc, + bool foldConstants) { if (!left || !right) return NULL; + /* + * Ensure that the parse tree is faithful to the source when "use asm" (for + * the purpose of type checking). + */ + if (pc->useAsmOrInsideUseAsm()) + return handler->new_(kind, op, left, right); + /* * Flatten a left-associative (left-heavy) tree of a given operator into * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion. diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 8dbceea1c8e..4bbdf95b352 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -590,7 +590,8 @@ struct ParseNode { */ static ParseNode * newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, - FullParseHandler *handler, bool foldConstants); + FullParseHandler *handler, ParseContext *pc, + bool foldConstants); inline PropertyName *name() const; inline JSAtom *atom() const; diff --git a/js/src/frontend/Parser-inl.h b/js/src/frontend/Parser-inl.h index b2dca7fc700..08ab5b07120 100644 --- a/js/src/frontend/Parser-inl.h +++ b/js/src/frontend/Parser-inl.h @@ -10,6 +10,8 @@ #include "frontend/Parser.h" +#include "frontend/SharedContext-inl.h" + namespace js { namespace frontend { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index e0f4121926b..1f388e76953 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -432,6 +432,8 @@ FunctionBox::FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fu ndefaults(0), inWith(false), // initialized below inGenexpLambda(false), + useAsm(false), + insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()), funCxFlags() { if (!outerpc) { @@ -1285,6 +1287,14 @@ Parser::leaveFunction(ParseNode *fn, HandlePropertyName funNam continue; } + /* + * If there are no uses of this placeholder (e.g., it was created + * for an identifierName that turned out to be a label), there is + * nothing left to do. + */ + if (!dn->dn_uses) + continue; + Definition *outer_dn = pc->decls().lookupFirst(atom); /* @@ -2112,6 +2122,13 @@ Parser::maybeParseDirective(Node pn, bool *cont) pc->sc->strict = true; } } + } else if (directive == context->names().useAsm) { + if (pc->sc->isFunctionBox()) { + pc->sc->asFunctionBox()->useAsm = true; + } else { + if (!report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL)) + return false; + } } } return true; @@ -4486,7 +4503,7 @@ Parser::variables(ParseNodeKind kind, bool *psimple, if (!init) return null(); - pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init); + pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc); if (!pn2) return null(); handler.addList(pn, pn2); @@ -4609,7 +4626,7 @@ BEGIN_EXPR_PARSER(mulExpr1) ? PNK_DIV : PNK_MOD; JSOp op = tokenStream.currentToken().t_op; - pn = handler.newBinaryOrAppend(kind, pn, unaryExpr(), op); + pn = handler.newBinaryOrAppend(kind, pn, unaryExpr(), pc, op); } return pn; } @@ -4622,7 +4639,7 @@ BEGIN_EXPR_PARSER(addExpr1) TokenKind tt = tokenStream.currentToken().type; JSOp op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB; ParseNodeKind kind = (tt == TOK_PLUS) ? PNK_ADD : PNK_SUB; - pn = handler.newBinaryOrAppend(kind, pn, mulExpr1n(), op); + pn = handler.newBinaryOrAppend(kind, pn, mulExpr1n(), pc, op); } return pn; } @@ -4697,7 +4714,7 @@ BEGIN_EXPR_PARSER(relExpr1) tokenStream.isCurrentTokenType(TOK_INSTANCEOF))) { ParseNodeKind kind = RelationalTokenToParseNodeKind(tokenStream.currentToken()); JSOp op = tokenStream.currentToken().t_op; - pn = handler.newBinaryOrAppend(kind, pn, shiftExpr1n(), op); + pn = handler.newBinaryOrAppend(kind, pn, shiftExpr1n(), pc, op); } /* Restore previous state of parsingForInit flag. */ pc->parsingForInit |= oldParsingForInit; @@ -4741,7 +4758,7 @@ BEGIN_EXPR_PARSER(bitAndExpr1) { Node pn = eqExpr1i(); while (pn && tokenStream.isCurrentTokenType(TOK_BITAND)) - pn = handler.newBinaryOrAppend(PNK_BITAND, pn, eqExpr1n(), JSOP_BITAND); + pn = handler.newBinaryOrAppend(PNK_BITAND, pn, eqExpr1n(), pc, JSOP_BITAND); return pn; } END_EXPR_PARSER(bitAndExpr1) @@ -4750,7 +4767,7 @@ BEGIN_EXPR_PARSER(bitXorExpr1) { Node pn = bitAndExpr1i(); while (pn && tokenStream.isCurrentTokenType(TOK_BITXOR)) - pn = handler.newBinaryOrAppend(PNK_BITXOR, pn, bitAndExpr1n(), JSOP_BITXOR); + pn = handler.newBinaryOrAppend(PNK_BITXOR, pn, bitAndExpr1n(), pc, JSOP_BITXOR); return pn; } END_EXPR_PARSER(bitXorExpr1) @@ -4759,7 +4776,7 @@ BEGIN_EXPR_PARSER(bitOrExpr1) { Node pn = bitXorExpr1i(); while (pn && tokenStream.isCurrentTokenType(TOK_BITOR)) - pn = handler.newBinaryOrAppend(PNK_BITOR, pn, bitXorExpr1n(), JSOP_BITOR); + pn = handler.newBinaryOrAppend(PNK_BITOR, pn, bitXorExpr1n(), pc, JSOP_BITOR); return pn; } END_EXPR_PARSER(bitOrExpr1) @@ -4768,7 +4785,7 @@ BEGIN_EXPR_PARSER(andExpr1) { Node pn = bitOrExpr1i(); while (pn && tokenStream.isCurrentTokenType(TOK_AND)) - pn = handler.newBinaryOrAppend(PNK_AND, pn, bitOrExpr1n(), JSOP_AND); + pn = handler.newBinaryOrAppend(PNK_AND, pn, bitOrExpr1n(), pc, JSOP_AND); return pn; } END_EXPR_PARSER(andExpr1) @@ -4779,7 +4796,7 @@ Parser::orExpr1() { Node pn = andExpr1i(); while (pn && tokenStream.isCurrentTokenType(TOK_OR)) - pn = handler.newBinaryOrAppend(PNK_OR, pn, andExpr1n(), JSOP_OR); + pn = handler.newBinaryOrAppend(PNK_OR, pn, andExpr1n(), pc, JSOP_OR); return pn; } @@ -4907,7 +4924,7 @@ Parser::assignExpr() if (!rhs) return null(); - return handler.newBinaryOrAppend(kind, lhs, rhs, op); + return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op); } template <> bool diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 431cccff28b..02aac640e84 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -217,6 +217,10 @@ struct ParseContext /* tree context for semantic checks */ // if (cond) { function f3() { if (cond) { function f4() { } } } } // bool atBodyLevel(); + + inline bool useAsmOrInsideUseAsm() const { + return sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm(); + } }; template diff --git a/js/src/frontend/SharedContext-inl.h b/js/src/frontend/SharedContext-inl.h index c2ac00cf540..74cac685efc 100644 --- a/js/src/frontend/SharedContext-inl.h +++ b/js/src/frontend/SharedContext-inl.h @@ -42,13 +42,6 @@ SharedContext::asModuleBox() return static_cast(this); } -inline FunctionBox * -SharedContext::asFunctionBox() -{ - JS_ASSERT(isFunctionBox()); - return static_cast(this); -} - GlobalSharedContext::GlobalSharedContext(JSContext *cx, JSObject *scopeChain, bool strict) : SharedContext(cx, strict), scopeChain_(cx, scopeChain) diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index d7f4ae71439..7099205abd5 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -204,6 +204,8 @@ class FunctionBox : public ObjectBox, public SharedContext uint16_t ndefaults; bool inWith:1; /* some enclosing scope is a with-statement */ bool inGenexpLambda:1; /* lambda from generator expression */ + bool useAsm:1; /* function contains "use asm" directive */ + bool insideUseAsm:1; /* nested function of function of "use asm" directive */ FunctionContextFlags funCxFlags; @@ -226,8 +228,21 @@ class FunctionBox : public ObjectBox, public SharedContext void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; } void setDefinitelyNeedsArgsObj() { JS_ASSERT(funCxFlags.argumentsHasLocalBinding); funCxFlags.definitelyNeedsArgsObj = true; } + + // Return whether this function has either specified "use asm" or is + // (transitively) nested inside a function that has. + bool useAsmOrInsideUseAsm() const { + return useAsm || insideUseAsm; + } }; +inline FunctionBox * +SharedContext::asFunctionBox() +{ + JS_ASSERT(isFunctionBox()); + return static_cast(this); +} + /* * NB: If you add a new type of statement that is a scope, add it between * STMT_WITH and STMT_CATCH, or you will break StmtInfoBase::linksScope. If you diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 22453632a14..e01250028be 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -72,7 +72,8 @@ class SyntaxParseHandler Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) { return NodeGeneric; } - Node newBinaryOrAppend(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) { + Node newBinaryOrAppend(ParseNodeKind kind, Node left, Node right, + ParseContext *pc, JSOp op = JSOP_NOP) { return NodeGeneric; } void setBinaryRHS(Node pn, Node rhs) {} diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 6497ec1bdda..fda8f76eafc 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -612,6 +612,15 @@ TokenStream::reportStrictWarningErrorNumberVA(const TokenPos &pos, unsigned erro return reportCompileErrorNumberVA(pos, JSREPORT_STRICT | JSREPORT_WARNING, errorNumber, args); } +void +TokenStream::reportAsmJSError(ParseNode *pn, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); + reportCompileErrorNumberVA(pn->pn_pos, JSREPORT_WARNING, errorNumber, args); + va_end(args); +} + /* * We have encountered a '\': check for a Unicode escape sequence after it. * Return 'true' and the character code value (by value) if we found a diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index b721bdb8305..a5284405bca 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -489,6 +489,9 @@ class TokenStream bool reportStrictWarningErrorNumberVA(const TokenPos &pos, unsigned errorNumber, va_list args); + // asm.js reporter + void reportAsmJSError(ParseNode *pn, unsigned errorNumber, ...); + private: // These are private because they should only be called by the tokenizer // while tokenizing not by, for example, BytecodeEmitter. diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 7d3ab086845..2fc654b6b83 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -331,6 +331,7 @@ typedef RelocatablePtr RelocatablePtrScript; typedef HeapPtr HeapPtrObject; typedef HeapPtr HeapPtrFunction; typedef HeapPtr HeapPtrString; +typedef HeapPtr HeapPtrPropertyName; typedef HeapPtr HeapPtrScript; typedef HeapPtr HeapPtrShape; typedef HeapPtr HeapPtrBaseShape; diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 4c944b205b1..68c4fa9b2b8 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -332,6 +332,7 @@ DeclMarkerImpl(Object, GlobalObject) DeclMarkerImpl(Object, JSObject) DeclMarkerImpl(Object, JSFunction) DeclMarkerImpl(Object, ScopeObject) +DeclMarkerImpl(Object, ArrayBufferObject) DeclMarkerImpl(Script, JSScript) DeclMarkerImpl(Shape, Shape) DeclMarkerImpl(String, JSAtom) diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index af2a90cd323..b0393990877 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -98,6 +98,7 @@ DeclMarker(Object, GlobalObject) DeclMarker(Object, JSObject) DeclMarker(Object, JSFunction) DeclMarker(Object, ScopeObject) +DeclMarker(Object, ArrayBufferObject) DeclMarker(Script, JSScript) DeclMarker(Shape, Shape) DeclMarker(String, JSAtom) diff --git a/js/src/ion/AsmJS.cpp b/js/src/ion/AsmJS.cpp new file mode 100644 index 00000000000..b9772a0808b --- /dev/null +++ b/js/src/ion/AsmJS.cpp @@ -0,0 +1,5007 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsmath.h" +#include "frontend/ParseNode.h" +#include "ion/AsmJS.h" +#include "ion/AsmJSModule.h" + +#include "frontend/ParseNode-inl.h" + +using namespace js; +using namespace js::frontend; +using namespace mozilla; + +#ifdef JS_ASMJS + +#include "ion/CodeGenerator.h" +#include "ion/MIR.h" +#include "ion/MIRGraph.h" + +using namespace js::ion; + +/*****************************************************************************/ +// ParseNode utilities + +static inline ParseNode * +NextNode(ParseNode *pn) +{ + return pn->pn_next; +} + +static inline ParseNode * +UnaryKid(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_UNARY)); + return pn->pn_kid; +} + +static inline ParseNode * +BinaryRight(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_BINARY)); + return pn->pn_right; +} + +static inline ParseNode * +BinaryLeft(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_BINARY)); + return pn->pn_left; +} + +static inline ParseNode * +TernaryKid1(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_TERNARY)); + return pn->pn_kid1; +} + +static inline ParseNode * +TernaryKid2(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_TERNARY)); + return pn->pn_kid2; +} + +static inline ParseNode * +TernaryKid3(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_TERNARY)); + return pn->pn_kid3; +} + +static inline ParseNode * +ListHead(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_LIST)); + return pn->pn_head; +} + +static inline unsigned +ListLength(ParseNode *pn) +{ + JS_ASSERT(pn->isArity(PN_LIST)); + return pn->pn_count; +} + +static inline ParseNode * +CallCallee(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_CALL)); + return ListHead(pn); +} + +static inline unsigned +CallArgListLength(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_CALL)); + JS_ASSERT(ListLength(pn) >= 1); + return ListLength(pn) - 1; +} + +static inline ParseNode * +CallArgList(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_CALL)); + return NextNode(ListHead(pn)); +} + +static inline ParseNode * +VarListHead(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_VAR)); + return ListHead(pn); +} + +static inline ParseNode * +CaseExpr(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT)); + return BinaryLeft(pn); +} + +static inline ParseNode * +CaseBody(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT)); + return BinaryRight(pn); +} + +static inline JSAtom * +StringAtom(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_STRING)); + return pn->pn_atom; +} + +static inline bool +IsExpressionStatement(ParseNode *pn) +{ + return pn->isKind(PNK_SEMI); +} + +static inline ParseNode * +ExpressionStatementExpr(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_SEMI)); + return UnaryKid(pn); +} + +static inline PropertyName * +LoopControlMaybeLabel(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_BREAK) || pn->isKind(PNK_CONTINUE)); + JS_ASSERT(pn->isArity(PN_NULLARY)); + return pn->as().label(); +} + +static inline PropertyName * +LabeledStatementLabel(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_COLON)); + return pn->pn_atom->asPropertyName(); +} + +static inline ParseNode * +LabeledStatementStatement(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_COLON)); + return pn->expr(); +} + +static double +NumberNodeValue(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_NUMBER)); + return pn->pn_dval; +} + +static bool +NumberNodeHasFrac(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_NUMBER)); + return pn->pn_u.number.decimalPoint == HasDecimal; +} + +static ParseNode * +DotBase(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_DOT)); + JS_ASSERT(pn->isArity(PN_NAME)); + return pn->expr(); +} + +static PropertyName * +DotMember(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_DOT)); + JS_ASSERT(pn->isArity(PN_NAME)); + return pn->pn_atom->asPropertyName(); +} + +static ParseNode * +ElemBase(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_ELEM)); + return BinaryLeft(pn); +} + +static ParseNode * +ElemIndex(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_ELEM)); + return BinaryRight(pn); +} + +static inline JSFunction * +FunctionObject(ParseNode *fn) +{ + JS_ASSERT(fn->isKind(PNK_FUNCTION)); + JS_ASSERT(fn->isArity(PN_CODE)); + return fn->pn_funbox->function(); +} + +static inline PropertyName * +FunctionName(ParseNode *fn) +{ + if (JSAtom *atom = FunctionObject(fn)->atom()) + return atom->asPropertyName(); + return NULL; +} + +static inline ParseNode * +FunctionArgsList(ParseNode *fn, unsigned *numFormals) +{ + JS_ASSERT(fn->isKind(PNK_FUNCTION)); + ParseNode *argsBody = fn->pn_body; + JS_ASSERT(argsBody->isKind(PNK_ARGSBODY)); + *numFormals = argsBody->pn_count - 1; + return ListHead(argsBody); +} + +static inline bool +FunctionHasStatementList(ParseNode *fn) +{ + JS_ASSERT(fn->isKind(PNK_FUNCTION)); + ParseNode *argsBody = fn->pn_body; + JS_ASSERT(argsBody->isKind(PNK_ARGSBODY)); + ParseNode *body = argsBody->last(); + return body->isKind(PNK_STATEMENTLIST); +} + +static inline ParseNode * +FunctionStatementList(ParseNode *fn) +{ + JS_ASSERT(FunctionHasStatementList(fn)); + return fn->pn_body->last(); +} + +static inline ParseNode * +FunctionLastStatementOrNull(ParseNode *fn) +{ + ParseNode *list = FunctionStatementList(fn); + return list->pn_count == 0 ? NULL : list->last(); +} + +static inline bool +IsNormalObjectField(JSContext *cx, ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_COLON)); + return pn->getOp() == JSOP_INITPROP && + BinaryLeft(pn)->isKind(PNK_NAME) && + BinaryLeft(pn)->name() != cx->names().proto; +} + +static inline PropertyName * +ObjectNormalFieldName(JSContext *cx, ParseNode *pn) +{ + JS_ASSERT(IsNormalObjectField(cx, pn)); + return BinaryLeft(pn)->name(); +} + +static inline ParseNode * +ObjectFieldInitializer(ParseNode *pn) +{ + JS_ASSERT(pn->isKind(PNK_COLON)); + return BinaryRight(pn); +} + +static inline bool +IsDefinition(ParseNode *pn) +{ + return pn->isKind(PNK_NAME) && pn->isDefn(); +} + +static inline ParseNode * +MaybeDefinitionInitializer(ParseNode *pn) +{ + JS_ASSERT(IsDefinition(pn)); + return pn->expr(); +} + +static inline bool +IsUseOfName(ParseNode *pn, PropertyName *name) +{ + return pn->isKind(PNK_NAME) && pn->name() == name; +} + +static inline ParseNode * +SkipEmptyStatements(ParseNode *pn) +{ + while (pn && pn->isKind(PNK_SEMI) && !UnaryKid(pn)) + pn = pn->pn_next; + return pn; +} + +static inline ParseNode * +NextNonEmptyStatement(ParseNode *pn) +{ + return SkipEmptyStatements(pn->pn_next); +} + +/*****************************************************************************/ + +// Respresents the type of a general asm.js expression. +class Type +{ + public: + enum Which { + Double, + Doublish, + Fixnum, + Int, + Signed, + Unsigned, + Intish, + Void + }; + + private: + Which which_; + + public: + Type() : which_(Which(-1)) {} + Type(Which w) : which_(w) {} + + bool operator==(Type rhs) const { return which_ == rhs.which_; } + bool operator!=(Type rhs) const { return which_ != rhs.which_; } + + bool isSigned() const { + return which_ == Signed || which_ == Fixnum; + } + + bool isUnsigned() const { + return which_ == Unsigned || which_ == Fixnum; + } + + bool isInt() const { + return isSigned() || isUnsigned() || which_ == Int; + } + + bool isIntish() const { + return isInt() || which_ == Intish; + } + + bool isDouble() const { + return which_ == Double; + } + + bool isDoublish() const { + return isDouble() || which_ == Doublish; + } + + bool isVoid() const { + return which_ == Void; + } + + bool isExtern() const { + return isDouble() || isSigned() || isUnsigned(); + } + + MIRType toMIRType() const { + switch (which_) { + case Double: + case Doublish: + return MIRType_Double; + case Fixnum: + case Int: + case Signed: + case Unsigned: + case Intish: + return MIRType_Int32; + case Void: + return MIRType_None; + } + JS_NOT_REACHED("Invalid Type"); + return MIRType_None; + } +}; + +// Represents the subset of Type that can be used as the return type of a +// function. +class RetType +{ + public: + enum Which { + Void = Type::Void, + Signed = Type::Signed, + Double = Type::Double + }; + + private: + Which which_; + + public: + RetType() {} + RetType(Which w) : which_(w) {} + RetType(AsmJSCoercion coercion) { + switch (coercion) { + case AsmJS_ToInt32: which_ = Signed; break; + case AsmJS_ToNumber: which_ = Double; break; + } + } + Which which() const { + return which_; + } + Type toType() const { + return Type::Which(which_); + } + AsmJSModule::ReturnType toModuleReturnType() const { + switch (which_) { + case Void: return AsmJSModule::Return_Void; + case Signed: return AsmJSModule::Return_Int32; + case Double: return AsmJSModule::Return_Double; + } + JS_NOT_REACHED("Unexpected return type"); + return AsmJSModule::Return_Void; + } + MIRType toMIRType() const { + switch (which_) { + case Void: return MIRType_None; + case Signed: return MIRType_Int32; + case Double: return MIRType_Double; + } + JS_NOT_REACHED("Unexpected return type"); + return MIRType_None; + } + bool operator==(RetType rhs) const { return which_ == rhs.which_; } + bool operator!=(RetType rhs) const { return which_ != rhs.which_; } +}; + +// Implements <: (subtype) operator when the rhs is an RetType +static inline bool +operator<=(Type lhs, RetType rhs) +{ + switch (rhs.which()) { + case RetType::Signed: return lhs.isSigned(); + case RetType::Double: return lhs == Type::Double; + case RetType::Void: return lhs == Type::Void; + } + JS_NOT_REACHED("Unexpected rhs type"); + return false; +} + +// Represents the subset of Type that can be used as a variable or +// argument's type. Note: AsmJSCoercion and VarType are kept separate to +// make very clear the signed/int distinction: a coercion may explicitly sign +// an *expression* but, when stored as a variable, this signedness information +// is explicitly thrown away by the asm.js type system. E.g., in +// +// function f(i) { +// i = i | 0; (1) +// if (...) +// i = foo() >>> 0; +// else +// i = bar() | 0; +// return i | 0; (2) +// +// the AsmJSCoercion of (1) is Signed (since | performs ToInt32) but, when +// translated to an VarType, the result is a plain Int since, as shown, it +// is legal to assign both Signed and Unsigned (or some other Int) values to +// it. For (2), the AsmJSCoercion is also Signed but, when translated to an +// RetType, the result is Signed since callers (asm.js and non-asm.js) can +// rely on the return value being Signed. +class VarType +{ + public: + enum Which { + Int = Type::Int, + Double = Type::Double + }; + + private: + Which which_; + + public: + VarType() + : which_(Which(-1)) {} + VarType(Which w) + : which_(w) {} + VarType(AsmJSCoercion coercion) { + switch (coercion) { + case AsmJS_ToInt32: which_ = Int; break; + case AsmJS_ToNumber: which_ = Double; break; + } + } + Which which() const { + return which_; + } + Type toType() const { + return Type::Which(which_); + } + MIRType toMIRType() const { + return which_ == Int ? MIRType_Int32 : MIRType_Double; + } + AsmJSCoercion toCoercion() const { + return which_ == Int ? AsmJS_ToInt32 : AsmJS_ToNumber; + } + static VarType FromMIRType(MIRType type) { + JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + return type == MIRType_Int32 ? Int : Double; + } + bool operator==(VarType rhs) const { return which_ == rhs.which_; } + bool operator!=(VarType rhs) const { return which_ != rhs.which_; } +}; + +// Implements <: (subtype) operator when the rhs is an VarType +static inline bool +operator<=(Type lhs, VarType rhs) +{ + switch (rhs.which()) { + case VarType::Int: return lhs.isInt(); + case VarType::Double: return lhs.isDouble(); + } + JS_NOT_REACHED("Unexpected rhs type"); + return false; +} + +// Passed from parent expressions to child expressions to indicate if and how +// the child expression's result will be coerced. While most type checking +// occurs bottom-up (with child expressions returning the type of the result +// and parents checking these types), FFI calls naturally want to know the +// parent's context to determine the appropriate result type. If a parent +// passes NoCoercion to an FFI all, then the FFI's return type will be "Void" +// which will cause a type error if the result is used. +// +// The other application of Use is to support the asm.js type rule which +// allows (a-b+c-d+e)|0 without intermediate conversions. The type system has +// only binary +/- nodes so we simulate the n-ary expression by having the +// outer parent +/- expression pass in Use::AddOrSub so that the inner +// expression knows to return type Int instead of Intish. +class Use +{ + public: + enum Which { + NoCoercion, + ToInt32, + ToNumber, + AddOrSub + }; + + private: + Which which_; + unsigned *pcount_; + + public: + Use() + : which_(Which(-1)), pcount_(NULL) {} + Use(Which w) + : which_(w), pcount_(NULL) { JS_ASSERT(w != AddOrSub); } + Use(unsigned *pcount) + : which_(AddOrSub), pcount_(pcount) {} + Which which() const { + return which_; + } + unsigned &addOrSubCount() const { + JS_ASSERT(which_ == AddOrSub); + return *pcount_; + } + Type toFFIReturnType() const { + switch (which_) { + case NoCoercion: return Type::Void; + case ToInt32: return Type::Intish; + case ToNumber: return Type::Doublish; + case AddOrSub: return Type::Void; + } + JS_NOT_REACHED("unexpected use type"); + return Type::Void; + } + MIRType toMIRType() const { + switch (which_) { + case NoCoercion: return MIRType_None; + case ToInt32: return MIRType_Int32; + case ToNumber: return MIRType_Double; + case AddOrSub: return MIRType_None; + } + JS_NOT_REACHED("unexpected use type"); + return MIRType_None; + } + bool operator==(Use rhs) const { return which_ == rhs.which_; } + bool operator!=(Use rhs) const { return which_ != rhs.which_; } +}; + +/*****************************************************************************/ +// Numeric literal utilities + +// Represents the type and value of an asm.js numeric literal. +// +// A literal is a double iff the literal contains an exponent or decimal point +// (even if the fractional part is 0). Otherwise, integers may be classified: +// fixnum: [0, 2^31) +// negative int: [-2^31, 0) +// big unsigned: [2^31, 2^32) +// out of range: otherwise +class NumLit +{ + public: + enum Which { + Fixnum = Type::Fixnum, + NegativeInt = Type::Signed, + BigUnsigned = Type::Unsigned, + Double = Type::Double, + OutOfRangeInt = -1 + }; + + private: + Which which_; + Value v_; + + public: + NumLit(Which w, Value v) + : which_(w), v_(v) + {} + + Which which() const { + return which_; + } + + int32_t toInt32() const { + JS_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned); + return v_.toInt32(); + } + + double toDouble() const { + return v_.toDouble(); + } + + Type type() const { + JS_ASSERT(which_ != OutOfRangeInt); + return Type::Which(which_); + } + + Value value() const { + JS_ASSERT(which_ != OutOfRangeInt); + return v_; + } +}; + +// Note: '-' is never rolled into the number; numbers are always positive and +// negations must be applied manually. +static bool +IsNumericLiteral(ParseNode *pn) +{ + return pn->isKind(PNK_NUMBER) || + (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER)); +} + +static NumLit +ExtractNumericLiteral(ParseNode *pn) +{ + JS_ASSERT(IsNumericLiteral(pn)); + ParseNode *numberNode; + double d; + if (pn->isKind(PNK_NEG)) { + numberNode = UnaryKid(pn); + d = -NumberNodeValue(numberNode); + } else { + numberNode = pn; + d = NumberNodeValue(numberNode); + } + + if (NumberNodeHasFrac(numberNode)) + return NumLit(NumLit::Double, DoubleValue(d)); + + int64_t i64 = int64_t(d); + if (d != double(i64)) + return NumLit(NumLit::OutOfRangeInt, UndefinedValue()); + + if (i64 >= 0) { + if (i64 <= INT32_MAX) + return NumLit(NumLit::Fixnum, Int32Value(i64)); + if (i64 <= UINT32_MAX) + return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64))); + return NumLit(NumLit::OutOfRangeInt, UndefinedValue()); + } + if (i64 >= INT32_MIN) + return NumLit(NumLit::NegativeInt, Int32Value(i64)); + return NumLit(NumLit::OutOfRangeInt, UndefinedValue()); +} + +static inline bool +IsLiteralUint32(ParseNode *pn, uint32_t *u32) +{ + if (!IsNumericLiteral(pn)) + return false; + + NumLit literal = ExtractNumericLiteral(pn); + switch (literal.which()) { + case NumLit::Fixnum: + case NumLit::BigUnsigned: + *u32 = uint32_t(literal.toInt32()); + return true; + case NumLit::NegativeInt: + case NumLit::Double: + case NumLit::OutOfRangeInt: + return false; + } + + JS_NOT_REACHED("Bad literal type"); +} + +static inline bool +IsBits32(ParseNode *pn, int32_t i) +{ + if (!IsNumericLiteral(pn)) + return false; + + NumLit literal = ExtractNumericLiteral(pn); + switch (literal.which()) { + case NumLit::Fixnum: + case NumLit::BigUnsigned: + case NumLit::NegativeInt: + return literal.toInt32() == i; + case NumLit::Double: + case NumLit::OutOfRangeInt: + return false; + } + + JS_NOT_REACHED("Bad literal type"); +} + +/*****************************************************************************/ +// Typed array utilities + +static Type +TypedArrayLoadType(ArrayBufferView::ViewType viewType) +{ + switch (viewType) { + case ArrayBufferView::TYPE_INT8: + case ArrayBufferView::TYPE_INT16: + case ArrayBufferView::TYPE_INT32: + case ArrayBufferView::TYPE_UINT8: + case ArrayBufferView::TYPE_UINT16: + case ArrayBufferView::TYPE_UINT32: + return Type::Intish; + case ArrayBufferView::TYPE_FLOAT32: + case ArrayBufferView::TYPE_FLOAT64: + return Type::Doublish; + default:; + } + JS_NOT_REACHED("Unexpected array type"); + return Type(); +} + +enum ArrayStoreEnum { + ArrayStore_Intish, + ArrayStore_Double +}; + +static ArrayStoreEnum +TypedArrayStoreType(ArrayBufferView::ViewType viewType) +{ + switch (viewType) { + case ArrayBufferView::TYPE_INT8: + case ArrayBufferView::TYPE_INT16: + case ArrayBufferView::TYPE_INT32: + case ArrayBufferView::TYPE_UINT8: + case ArrayBufferView::TYPE_UINT16: + case ArrayBufferView::TYPE_UINT32: + return ArrayStore_Intish; + case ArrayBufferView::TYPE_FLOAT32: + case ArrayBufferView::TYPE_FLOAT64: + return ArrayStore_Double; + default:; + } + JS_NOT_REACHED("Unexpected array type"); + return ArrayStore_Double; +} + +/*****************************************************************************/ + +typedef Vector LabelVector; +typedef Vector CaseVector; + +// ModuleCompiler encapsulates the compilation of an entire asm.js module. Over +// the course of an ModuleCompiler object's lifetime, many FunctionCompiler +// objects will be created and destroyed in sequence, one for each function in +// the module. +// +// *** asm.js FFI calls *** +// +// asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type +// system does not place any constraints on the FFI call. In particular: +// - an FFI call's target is not known or speculated at module-compile time; +// - a single external function can be called with different signatures. +// +// If performance didn't matter, all FFI calls could simply box their arguments +// and call js::Invoke. However, we'd like to be able to specialize FFI calls +// to be more efficient in several cases: +// +// - for calls to JS functions which have been jitted, we'd like to call +// directly into JIT code without going through C++. +// +// - for calls to certain builtins, we'd like to be call directly into the C++ +// code for the builtin without going through the general call path. +// +// All of this requires dynamic specialization techniques which must happen +// after module compilation. To support this, at module-compilation time, each +// FFI call generates a call signature according to the system ABI, as if the +// callee was a C++ function taking/returning the same types as the caller was +// passing/expecting. The callee is loaded from a fixed offset in the global +// data array which allows the callee to change at runtime. Initially, the +// callee is stub which boxes its arguments and calls js::Invoke. +// +// To do this, we need to generate a callee stub for each pairing of FFI callee +// and signature. We call this pairing an "exit". For example, this code has +// two external functions and three exits: +// +// function f(global, imports) { +// "use asm"; +// var foo = imports.foo; +// var bar = imports.bar; +// function g() { +// foo(1); // Exit #1: (int) -> void +// foo(1.5); // Exit #2: (double) -> void +// bar(1)|0; // Exit #3: (int) -> int +// bar(2)|0; // Exit #3: (int) -> int +// } +// +// The ModuleCompiler maintains a hash table (ExitMap) which allows a call site +// to add a new exit or reuse an existing one. The key is an ExitDescriptor +// (which holds the exit pairing) and the value is an index into the +// Vector stored in the AsmJSModule. +class ModuleCompiler +{ + public: + class Func + { + ParseNode *fn_; + ParseNode *body_; + MIRTypeVector argTypes_; + RetType returnType_; + mutable Label code_; + + public: + Func(ParseNode *fn, ParseNode *body, MoveRef types, RetType returnType) + : fn_(fn), + body_(body), + argTypes_(types), + returnType_(returnType), + code_() + {} + + Func(MoveRef rhs) + : fn_(rhs->fn_), + body_(rhs->body_), + argTypes_(Move(rhs->argTypes_)), + returnType_(rhs->returnType_), + code_(rhs->code_) + {} + + ~Func() + { + // Avoid spurious Label assertions on compilation failure. + if (!code_.bound()) + code_.bind(0); + } + + ParseNode *fn() const { return fn_; } + ParseNode *body() const { return body_; } + unsigned numArgs() const { return argTypes_.length(); } + VarType argType(unsigned i) const { return VarType::FromMIRType(argTypes_[i]); } + const MIRTypeVector &argMIRTypes() const { return argTypes_; } + RetType returnType() const { return returnType_; } + Label *codeLabel() const { return &code_; } + }; + + class Global + { + public: + enum Which { Variable, Function, FuncPtrTable, FFI, ArrayView, MathBuiltin, Constant }; + + private: + Which which_; + union { + struct { + uint32_t index_; + VarType::Which type_; + } var; + uint32_t funcIndex_; + uint32_t funcPtrTableIndex_; + uint32_t ffiIndex_; + ArrayBufferView::ViewType viewType_; + AsmJSMathBuiltin mathBuiltin_; + double constant_; + } u; + + friend class ModuleCompiler; + + Global(Which which) : which_(which) {} + + public: + Which which() const { + return which_; + } + VarType varType() const { + JS_ASSERT(which_ == Variable); + return VarType(u.var.type_); + } + uint32_t varIndex() const { + JS_ASSERT(which_ == Variable); + return u.var.index_; + } + uint32_t funcIndex() const { + JS_ASSERT(which_ == Function); + return u.funcIndex_; + } + uint32_t funcPtrTableIndex() const { + JS_ASSERT(which_ == FuncPtrTable); + return u.funcPtrTableIndex_; + } + unsigned ffiIndex() const { + JS_ASSERT(which_ == FFI); + return u.ffiIndex_; + } + ArrayBufferView::ViewType viewType() const { + JS_ASSERT(which_ == ArrayView); + return u.viewType_; + } + AsmJSMathBuiltin mathBuiltin() const { + JS_ASSERT(which_ == MathBuiltin); + return u.mathBuiltin_; + } + double constant() const { + JS_ASSERT(which_ == Constant); + return u.constant_; + } + }; + + typedef Vector FuncPtrVector; + + class FuncPtrTable + { + FuncPtrVector elems_; + unsigned baseIndex_; + + public: + FuncPtrTable(MoveRef elems, unsigned baseIndex) + : elems_(elems), baseIndex_(baseIndex) {} + FuncPtrTable(MoveRef rhs) + : elems_(Move(rhs->elems_)), baseIndex_(rhs->baseIndex_) {} + + const Func &sig() const { return *elems_[0]; } + unsigned numElems() const { return elems_.length(); } + const Func &elem(unsigned i) const { return *elems_[i]; } + unsigned baseIndex() const { return baseIndex_; } + unsigned mask() const { JS_ASSERT(IsPowerOfTwo(numElems())); return numElems() - 1; } + }; + + typedef Vector FuncPtrTableVector; + + class ExitDescriptor + { + PropertyName *name_; + MIRTypeVector argTypes_; + Use use_; + + public: + ExitDescriptor(PropertyName *name, MoveRef argTypes, Use use) + : name_(name), + argTypes_(argTypes), + use_(use) + {} + ExitDescriptor(MoveRef rhs) + : name_(rhs->name_), + argTypes_(Move(rhs->argTypes_)), + use_(rhs->use_) + {} + const MIRTypeVector &argTypes() const { + return argTypes_; + } + Use use() const { + return use_; + } + + // ExitDescriptor is a HashPolicy: + typedef ExitDescriptor Lookup; + static HashNumber hash(const ExitDescriptor &d) { + HashNumber hn = HashGeneric(d.name_, d.use_.which()); + for (unsigned i = 0; i < d.argTypes_.length(); i++) + hn = AddToHash(hn, d.argTypes_[i]); + return hn; + } + static bool match(const ExitDescriptor &lhs, const ExitDescriptor &rhs) { + if (lhs.name_ != rhs.name_ || + lhs.argTypes_.length() != rhs.argTypes_.length() || + lhs.use_ != rhs.use_) + { + return false; + } + for (unsigned i = 0; i < lhs.argTypes_.length(); i++) { + if (lhs.argTypes_[i] != rhs.argTypes_[i]) + return false; + } + return true; + } + }; + + typedef HashMap ExitMap; + + private: + typedef HashMap MathNameMap; + typedef HashMap GlobalMap; + typedef Vector FuncVector; + typedef Vector GlobalAccessVector; + + JSContext * cx_; + LifoAlloc lifo_; + TempAllocator alloc_; + IonContext ionContext_; + MacroAssembler masm_; + + ScopedJSDeletePtr module_; + + PropertyName * moduleFunctionName_; + PropertyName * globalArgumentName_; + PropertyName * importArgumentName_; + PropertyName * bufferArgumentName_; + + GlobalMap globals_; + FuncVector functions_; + FuncPtrTableVector funcPtrTables_; + ExitMap exits_; + MathNameMap standardLibraryMathNames_; + + GlobalAccessVector globalAccesses_; + + Label stackOverflowLabel_; + Label operationCallbackLabel_; + + const char * errorString_; + ParseNode * errorNode_; + TokenStream & tokenStream_; + + DebugOnly currentPass_; + + bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltin builtin) { + JSAtom *atom = Atomize(cx_, name, strlen(name)); + if (!atom) + return false; + return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin); + } + + static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; + + public: + ModuleCompiler(JSContext *cx, TokenStream &ts) + : cx_(cx), + lifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + alloc_(&lifo_), + ionContext_(cx, cx->compartment, &alloc_), + masm_(), + moduleFunctionName_(NULL), + globalArgumentName_(NULL), + importArgumentName_(NULL), + bufferArgumentName_(NULL), + globals_(cx), + functions_(cx), + funcPtrTables_(cx), + exits_(cx), + standardLibraryMathNames_(cx), + globalAccesses_(cx), + errorString_(NULL), + errorNode_(NULL), + tokenStream_(ts), + currentPass_(1) + {} + + ~ModuleCompiler() { + if (errorString_) + tokenStream_.reportAsmJSError(errorNode_, JSMSG_USE_ASM_TYPE_FAIL, errorString_); + + // Avoid spurious Label assertions on compilation failure. + if (!stackOverflowLabel_.bound()) + stackOverflowLabel_.bind(0); + if (!operationCallbackLabel_.bound()) + operationCallbackLabel_.bind(0); + } + + bool init() { + if (!cx_->compartment->ensureIonCompartmentExists(cx_)) + return false; + + if (!globals_.init() || !exits_.init()) + return false; + + if (!standardLibraryMathNames_.init() || + !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) || + !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) || + !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) || + !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) || + !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) || + !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) || + !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) || + !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) || + !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) || + !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) || + !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) || + !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) || + !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) || + !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) || + !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul)) + { + return false; + } + + module_ = cx_->new_(); + if (!module_) + return false; + + return true; + } + + bool fail(const char *str, ParseNode *pn) { + JS_ASSERT(!errorString_); + JS_ASSERT(!errorNode_); + JS_ASSERT(str); + JS_ASSERT(pn); + errorString_ = str; + errorNode_ = pn; + return false; + } + + /*************************************************** Read-only interface */ + + JSContext *cx() const { return cx_; } + LifoAlloc &lifo() { return lifo_; } + TempAllocator &alloc() { return alloc_; } + MacroAssembler &masm() { return masm_; } + Label &stackOverflowLabel() { return stackOverflowLabel_; } + Label &operationCallbackLabel() { return operationCallbackLabel_; } + bool hasError() const { return errorString_ != NULL; } + const AsmJSModule &module() const { return *module_.get(); } + + PropertyName *moduleFunctionName() const { return moduleFunctionName_; } + PropertyName *globalArgumentName() const { return globalArgumentName_; } + PropertyName *importArgumentName() const { return importArgumentName_; } + PropertyName *bufferArgumentName() const { return bufferArgumentName_; } + + const Global *lookupGlobal(PropertyName *name) const { + if (GlobalMap::Ptr p = globals_.lookup(name)) + return &p->value; + return NULL; + } + const FuncPtrTable *lookupFuncPtrTable(PropertyName *name) const { + if (GlobalMap::Ptr p = globals_.lookup(name)) { + if (p->value.which() == Global::FuncPtrTable) + return &funcPtrTables_[p->value.funcPtrTableIndex()]; + } + return NULL; + } + const Func *lookupFunction(PropertyName *name) const { + if (GlobalMap::Ptr p = globals_.lookup(name)) { + if (p->value.which() == Global::Function) + return &functions_[p->value.funcIndex()]; + } + return NULL; + } + unsigned numFunctions() const { + return functions_.length(); + } + const Func &function(unsigned i) const { + return functions_[i]; + } + bool lookupStandardLibraryMathName(PropertyName *name, AsmJSMathBuiltin *mathBuiltin) const { + if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) { + *mathBuiltin = p->value; + return true; + } + return false; + } + ExitMap::Range allExits() const { + return exits_.all(); + } + + /***************************************************** Mutable interface */ + + void initModuleFunctionName(PropertyName *n) { moduleFunctionName_ = n; } + void initGlobalArgumentName(PropertyName *n) { globalArgumentName_ = n; } + void initImportArgumentName(PropertyName *n) { importArgumentName_ = n; } + void initBufferArgumentName(PropertyName *n) { bufferArgumentName_ = n; } + + bool addGlobalVarInitConstant(PropertyName *varName, VarType type, const Value &v) { + JS_ASSERT(currentPass_ == 1); + Global g(Global::Variable); + uint32_t index; + if (!module_->addGlobalVarInitConstant(v, &index)) + return false; + g.u.var.index_ = index; + g.u.var.type_ = type.which(); + return globals_.putNew(varName, g); + } + bool addGlobalVarImport(PropertyName *varName, PropertyName *fieldName, AsmJSCoercion coercion) + { + JS_ASSERT(currentPass_ == 1); + Global g(Global::Variable); + uint32_t index; + if (!module_->addGlobalVarImport(fieldName, coercion, &index)) + return false; + g.u.var.index_ = index; + g.u.var.type_ = VarType(coercion).which(); + return globals_.putNew(varName, g); + } + bool addFunction(MoveRef func) { + JS_ASSERT(currentPass_ == 1); + Global g(Global::Function); + g.u.funcIndex_ = functions_.length(); + if (!globals_.putNew(FunctionName(func->fn()), g)) + return false; + return functions_.append(func); + } + bool addFuncPtrTable(PropertyName *varName, MoveRef funcPtrs) { + JS_ASSERT(currentPass_ == 1); + Global g(Global::FuncPtrTable); + g.u.funcPtrTableIndex_ = funcPtrTables_.length(); + if (!globals_.putNew(varName, g)) + return false; + FuncPtrTable table(funcPtrs, module_->numFuncPtrTableElems()); + if (!module_->incrementNumFuncPtrTableElems(table.numElems())) + return false; + return funcPtrTables_.append(Move(table)); + } + bool addFFI(PropertyName *varName, PropertyName *field) { + JS_ASSERT(currentPass_ == 1); + Global g(Global::FFI); + uint32_t index; + if (!module_->addFFI(field, &index)) + return false; + g.u.ffiIndex_ = index; + return globals_.putNew(varName, g); + } + bool addArrayView(PropertyName *varName, ArrayBufferView::ViewType vt, PropertyName *fieldName) { + JS_ASSERT(currentPass_ == 1); + Global g(Global::ArrayView); + if (!module_->addArrayView(vt, fieldName)) + return false; + g.u.viewType_ = vt; + return globals_.putNew(varName, g); + } + bool addMathBuiltin(PropertyName *varName, AsmJSMathBuiltin mathBuiltin, PropertyName *fieldName) { + JS_ASSERT(currentPass_ == 1); + if (!module_->addMathBuiltin(mathBuiltin, fieldName)) + return false; + Global g(Global::MathBuiltin); + g.u.mathBuiltin_ = mathBuiltin; + return globals_.putNew(varName, g); + } + bool addGlobalConstant(PropertyName *varName, double constant, PropertyName *fieldName) { + JS_ASSERT(currentPass_ == 1); + if (!module_->addGlobalConstant(constant, fieldName)) + return false; + Global g(Global::Constant); + g.u.constant_ = constant; + return globals_.putNew(varName, g); + } + bool collectAccesses(MIRGenerator &gen) { + if (!module_->addHeapAccesses(gen.heapAccesses())) + return false; + + for (unsigned i = 0; i < gen.globalAccesses().length(); i++) { + if (!globalAccesses_.append(gen.globalAccesses()[i])) + return false; + } + return true; + } + bool addGlobalAccess(AsmJSGlobalAccess access) { + return globalAccesses_.append(access); + } + bool addExportedFunction(const Func *func, PropertyName *maybeFieldName) { + JS_ASSERT(currentPass_ == 1); + AsmJSModule::ArgCoercionVector argCoercions; + if (!argCoercions.resize(func->numArgs())) + return false; + for (unsigned i = 0; i < func->numArgs(); i++) + argCoercions[i] = func->argType(i).toCoercion(); + AsmJSModule::ReturnType returnType = func->returnType().toModuleReturnType(); + return module_->addExportedFunction(FunctionObject(func->fn()), maybeFieldName, + Move(argCoercions), returnType); + } + + void setFirstPassComplete() { + JS_ASSERT(currentPass_ == 1); + currentPass_ = 2; + } + + Func &function(unsigned funcIndex) { + JS_ASSERT(currentPass_ == 2); + return functions_[funcIndex]; + } + bool addExit(unsigned ffiIndex, PropertyName *name, MoveRef argTypes, Use use, + unsigned *exitIndex) + { + JS_ASSERT(currentPass_ == 2); + ExitDescriptor exitDescriptor(name, argTypes, use); + ExitMap::AddPtr p = exits_.lookupForAdd(exitDescriptor); + if (p) { + *exitIndex = p->value; + return true; + } + if (!module_->addExit(ffiIndex, exitIndex)) + return false; + return exits_.add(p, Move(exitDescriptor), *exitIndex); + } + + + void setSecondPassComplete() { + JS_ASSERT(currentPass_ == 2); + masm_.align(gc::PageSize); + module_->setFunctionBytes(masm_.size()); + currentPass_ = 3; + } + + void setExitOffset(unsigned exitIndex) { + JS_ASSERT(currentPass_ == 3); + module_->exit(exitIndex).initCodeOffset(masm_.size()); + } + void setEntryOffset(unsigned exportIndex) { + JS_ASSERT(currentPass_ == 3); + module_->exportedFunction(exportIndex).initCodeOffset(masm_.size()); + } + + bool finish(ScopedJSDeletePtr *module) { + // After finishing, the only valid operation on an ModuleCompiler is + // destruction. + JS_ASSERT(currentPass_ == 3); + currentPass_ = -1; + + // Finish the code section. + masm_.finish(); + if (masm_.oom()) + return false; + + // The global data section sits immediately after the executable (and + // other) data allocated by the MacroAssembler. Round up bytesNeeded so + // that doubles/pointers stay aligned. + size_t codeBytes = AlignBytes(masm_.bytesNeeded(), sizeof(double)); + size_t totalBytes = codeBytes + module_->globalDataBytes(); + + // The code must be page aligned, so include extra space so that we can + // AlignBytes the allocation result below. + size_t allocedBytes = totalBytes + gc::PageSize; + + // Allocate the slab of memory. + JSC::ExecutableAllocator *execAlloc = cx_->compartment->ionCompartment()->execAlloc(); + JSC::ExecutablePool *pool; + uint8_t *unalignedBytes = (uint8_t*)execAlloc->alloc(allocedBytes, &pool, JSC::ASMJS_CODE); + if (!unalignedBytes) + return false; + uint8_t *code = (uint8_t*)AlignBytes((uintptr_t)unalignedBytes, gc::PageSize); + + // The ExecutablePool owns the memory and must be released by the AsmJSModule. + module_->takeOwnership(pool, code, codeBytes, totalBytes); + + // Copy the buffer into executable memory (c.f. IonCode::copyFrom). + masm_.executableCopy(code); + masm_.processCodeLabels(code); + JS_ASSERT(masm_.jumpRelocationTableBytes() == 0); + JS_ASSERT(masm_.dataRelocationTableBytes() == 0); + JS_ASSERT(masm_.preBarrierTableBytes() == 0); + JS_ASSERT(!masm_.hasEnteredExitFrame()); + + // Patch everything that needs an absolute address: + + // Entry points + for (unsigned i = 0; i < module_->numExportedFunctions(); i++) + module_->exportedFunction(i).patch(code); + + // Exit points + for (unsigned i = 0; i < module_->numExits(); i++) { + module_->exit(i).patch(code); + module_->exitIndexToGlobalDatum(i).exit = module_->exit(i).code(); + module_->exitIndexToGlobalDatum(i).fun = NULL; + } + module_->setOperationCallbackExit(code + masm_.actualOffset(operationCallbackLabel_.offset())); + + // Function-pointer table entries + unsigned elemIndex = 0; + for (unsigned i = 0; i < funcPtrTables_.length(); i++) { + FuncPtrTable &table = funcPtrTables_[i]; + JS_ASSERT(elemIndex == table.baseIndex()); + for (unsigned j = 0; j < table.numElems(); j++) { + uint8_t *funcPtr = code + masm_.actualOffset(table.elem(j).codeLabel()->offset()); + module_->funcPtrIndexToGlobalDatum(elemIndex++) = funcPtr; + } + JS_ASSERT(elemIndex == table.baseIndex() + table.numElems()); + } + JS_ASSERT(elemIndex == module_->numFuncPtrTableElems()); + + // Global accesses in function bodies + for (unsigned i = 0; i < globalAccesses_.length(); i++) { + AsmJSGlobalAccess access = globalAccesses_[i]; + masm_.patchAsmJSGlobalAccess(access.offset, code, codeBytes, access.globalDataOffset); + } + + // The AsmJSHeapAccess offsets need to be updated to reflect the + // "actualOffset" (an ARM distinction). + for (unsigned i = 0; i < module_->numHeapAccesses(); i++) { + AsmJSHeapAccess &access = module_->heapAccess(i); + access.updateOffset(masm_.actualOffset(access.offset())); + } + + *module = module_.forget(); + return true; + } +}; + +/*****************************************************************************/ + +// Encapsulates the compilation of a single function in an asm.js module. The +// function compiler handles the creation and final backend compilation of the +// MIR graph. Also see ModuleCompiler comment. +class FunctionCompiler +{ + public: + struct Local + { + enum Which { Var, Arg } which; + VarType type; + unsigned slot; + Value initialValue; + + Local(VarType t, unsigned slot) + : which(Arg), type(t), slot(slot), initialValue(MagicValue(JS_GENERIC_MAGIC)) {} + Local(VarType t, unsigned slot, const Value &init) + : which(Var), type(t), slot(slot), initialValue(init) {} + }; + typedef HashMap LocalMap; + + private: + typedef Vector BlockVector; + typedef HashMap LabeledBlockMap; + typedef HashMap UnlabeledBlockMap; + typedef Vector NodeStack; + + ModuleCompiler & m_; + ModuleCompiler::Func & func_; + LocalMap locals_; + + LifoAllocScope lifoAllocScope_; + MIRGraph mirGraph_; + MIRGenerator mirGen_; + CompileInfo compileInfo_; + AutoFlushCache autoFlushCache_; + + MBasicBlock * curBlock_; + NodeStack loopStack_; + NodeStack breakableStack_; + UnlabeledBlockMap unlabeledBreaks_; + UnlabeledBlockMap unlabeledContinues_; + LabeledBlockMap labeledBreaks_; + LabeledBlockMap labeledContinues_; + + public: + FunctionCompiler(ModuleCompiler &m, ModuleCompiler::Func &func, MoveRef locals) + : m_(m), + func_(func), + locals_(locals), + lifoAllocScope_(&m.lifo()), + mirGraph_(&m.alloc()), + mirGen_(m.cx()->compartment, &m.alloc(), &mirGraph_, &compileInfo_), + compileInfo_(locals_.count()), + autoFlushCache_("asm.js"), + curBlock_(NULL), + loopStack_(m.cx()), + breakableStack_(m.cx()), + unlabeledBreaks_(m.cx()), + unlabeledContinues_(m.cx()), + labeledBreaks_(m.cx()), + labeledContinues_(m.cx()) + {} + + bool init() + { + if (!unlabeledBreaks_.init() || + !unlabeledContinues_.init() || + !labeledBreaks_.init() || + !labeledContinues_.init()) + { + return false; + } + + if (!newBlock(/* pred = */ NULL, &curBlock_)) + return false; + + curBlock_->add(MAsmJSCheckOverRecursed::New(&m_.stackOverflowLabel())); + + for (ABIArgIter i(func_.argMIRTypes()); !i.done(); i++) { + MAsmJSParameter *ins = MAsmJSParameter::New(*i, i.mirType()); + curBlock_->add(ins); + curBlock_->initSlot(compileInfo_.localSlot(i.index()), ins); + } + + for (LocalMap::Range r = locals_.all(); !r.empty(); r.popFront()) { + const Local &local = r.front().value; + if (local.which == Local::Var) { + MConstant *ins = MConstant::New(local.initialValue); + curBlock_->add(ins); + curBlock_->initSlot(compileInfo_.localSlot(local.slot), ins); + } + } + + return true; + } + + bool fail(const char *str, ParseNode *pn) + { + m_.fail(str, pn); + return false; + } + + ~FunctionCompiler() + { + if (!m().hasError() && !cx()->isExceptionPending()) { + JS_ASSERT(loopStack_.empty()); + JS_ASSERT(unlabeledBreaks_.empty()); + JS_ASSERT(unlabeledContinues_.empty()); + JS_ASSERT(labeledBreaks_.empty()); + JS_ASSERT(labeledContinues_.empty()); + JS_ASSERT(curBlock_ == NULL); + } + } + + /*************************************************** Read-only interface */ + + JSContext * cx() const { return m_.cx(); } + ModuleCompiler & m() const { return m_; } + const AsmJSModule & module() const { return m_.module(); } + ModuleCompiler::Func & func() const { return func_; } + MIRGraph & mirGraph() { return mirGraph_; } + MIRGenerator & mirGen() { return mirGen_; } + + const Local *lookupLocal(PropertyName *name) const + { + if (LocalMap::Ptr p = locals_.lookup(name)) + return &p->value; + return NULL; + } + + MDefinition *getLocalDef(const Local &local) + { + if (!curBlock_) + return NULL; + return curBlock_->getSlot(compileInfo_.localSlot(local.slot)); + } + + const ModuleCompiler::Func *lookupFunction(PropertyName *name) const + { + if (locals_.has(name)) + return NULL; + if (const ModuleCompiler::Func *func = m_.lookupFunction(name)) + return func; + return NULL; + } + + const ModuleCompiler::Global *lookupGlobal(PropertyName *name) const + { + if (locals_.has(name)) + return NULL; + return m_.lookupGlobal(name); + } + + /************************************************* Expression generation */ + + MDefinition *constant(const Value &v) + { + if (!curBlock_) + return NULL; + JS_ASSERT(v.isNumber()); + MConstant *constant = MConstant::New(v); + curBlock_->add(constant); + return constant; + } + + template + MDefinition *unary(MDefinition *op) + { + if (!curBlock_) + return NULL; + T *ins = T::NewAsmJS(op); + curBlock_->add(ins); + return ins; + } + + template + MDefinition *unary(MDefinition *op, MIRType type) + { + if (!curBlock_) + return NULL; + T *ins = T::NewAsmJS(op, type); + curBlock_->add(ins); + return ins; + } + + template + MDefinition *binary(MDefinition *lhs, MDefinition *rhs) + { + if (!curBlock_) + return NULL; + T *ins = T::New(lhs, rhs); + curBlock_->add(ins); + return ins; + } + + template + MDefinition *binary(MDefinition *lhs, MDefinition *rhs, MIRType type) + { + if (!curBlock_) + return NULL; + T *ins = T::NewAsmJS(lhs, rhs, type); + curBlock_->add(ins); + return ins; + } + + MDefinition *mul(MDefinition *lhs, MDefinition *rhs, MIRType type, MMul::Mode mode) + { + if (!curBlock_) + return NULL; + MMul *ins = MMul::New(lhs, rhs, type, mode); + curBlock_->add(ins); + return ins; + } + + template + MDefinition *bitwise(MDefinition *lhs, MDefinition *rhs) + { + if (!curBlock_) + return NULL; + T *ins = T::NewAsmJS(lhs, rhs); + curBlock_->add(ins); + return ins; + } + + template + MDefinition *bitwise(MDefinition *op) + { + if (!curBlock_) + return NULL; + T *ins = T::NewAsmJS(op); + curBlock_->add(ins); + return ins; + } + + MDefinition *compare(MDefinition *lhs, MDefinition *rhs, JSOp op, MCompare::CompareType type) + { + if (!curBlock_) + return NULL; + MCompare *ins = MCompare::NewAsmJS(lhs, rhs, op, type); + curBlock_->add(ins); + return ins; + } + + void assign(const Local &local, MDefinition *def) + { + if (!curBlock_) + return; + curBlock_->setSlot(compileInfo_.localSlot(local.slot), def); + } + + MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr) + { + if (!curBlock_) + return NULL; + MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(vt, ptr); + curBlock_->add(load); + return load; + } + + void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v) + { + if (!curBlock_) + return; + curBlock_->add(MAsmJSStoreHeap::New(vt, ptr, v)); + } + + MDefinition *loadGlobalVar(const ModuleCompiler::Global &global) + { + if (!curBlock_) + return NULL; + MIRType type = global.varType().toMIRType(); + unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varIndex()); + MAsmJSLoadGlobalVar *load = MAsmJSLoadGlobalVar::New(type, globalDataOffset); + curBlock_->add(load); + return load; + } + + void storeGlobalVar(const ModuleCompiler::Global &global, MDefinition *v) + { + if (!curBlock_) + return; + unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varIndex()); + curBlock_->add(MAsmJSStoreGlobalVar::New(globalDataOffset, v)); + } + + /***************************************************************** Calls */ + + // The IonMonkey backend maintains a single stack offset (from the stack + // pointer to the base of the frame) by adding the total amount of spill + // space required plus the maximum stack required for argument passing. + // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must + // manually accumulate, for the entire function, the maximum required stack + // space for argument passing. (This is passed to the CodeGenerator via + // MIRGenerator::maxAsmJSStackArgBytes.) Naively, this would just be the + // maximum of the stack space required for each individual call (as + // determined by the call ABI). However, as an optimization, arguments are + // stored to the stack immediately after evaluation (to decrease live + // ranges and reduce spilling). This introduces the complexity that, + // between evaluating an argument and making the call, another argument + // evaluation could perform a call that also needs to store to the stack. + // When this occurs childClobbers_ = true and the parent expression's + // arguments are stored above the maximum depth clobbered by a child + // expression. + + class Args + { + ABIArgGenerator abi_; + uint32_t prevMaxStackBytes_; + uint32_t maxChildStackBytes_; + uint32_t spIncrement_; + Vector types_; + MAsmJSCall::Args regArgs_; + Vector stackArgs_; + bool childClobbers_; + + friend class FunctionCompiler; + + public: + Args(FunctionCompiler &f) + : prevMaxStackBytes_(0), + maxChildStackBytes_(0), + spIncrement_(0), + types_(f.cx()), + regArgs_(f.cx()), + stackArgs_(f.cx()), + childClobbers_(false) + {} + unsigned length() const { + return types_.length(); + } + Type type(unsigned i) const { + return types_[i]; + } + }; + + void startCallArgs(Args *args) + { + if (!curBlock_) + return; + args->prevMaxStackBytes_ = mirGen_.resetAsmJSMaxStackArgBytes(); + } + + bool passArg(MDefinition *argDef, Type type, Args *args) + { + if (!args->types_.append(type)) + return false; + + if (!curBlock_) + return true; + + uint32_t childStackBytes = mirGen_.resetAsmJSMaxStackArgBytes(); + args->maxChildStackBytes_ = Max(args->maxChildStackBytes_, childStackBytes); + if (childStackBytes > 0 && !args->stackArgs_.empty()) + args->childClobbers_ = true; + + ABIArg arg = args->abi_.next(type.toMIRType()); + if (arg.kind() == ABIArg::Stack) { + MAsmJSPassStackArg *mir = MAsmJSPassStackArg::New(arg.offsetFromArgBase(), argDef); + curBlock_->add(mir); + if (!args->stackArgs_.append(mir)) + return false; + } else { + if (!args->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef))) + return false; + } + return true; + } + + void finishCallArgs(Args *args) + { + if (!curBlock_) + return; + uint32_t parentStackBytes = args->abi_.stackBytesConsumedSoFar(); + uint32_t newStackBytes; + if (args->childClobbers_) { + args->spIncrement_ = AlignBytes(args->maxChildStackBytes_, StackAlignment); + for (unsigned i = 0; i < args->stackArgs_.length(); i++) + args->stackArgs_[i]->incrementOffset(args->spIncrement_); + newStackBytes = Max(args->prevMaxStackBytes_, + args->spIncrement_ + parentStackBytes); + } else { + args->spIncrement_ = 0; + newStackBytes = Max(args->prevMaxStackBytes_, + Max(args->maxChildStackBytes_, parentStackBytes)); + } + mirGen_.setAsmJSMaxStackArgBytes(newStackBytes); + } + + private: + bool call(MAsmJSCall::Callee callee, const Args &args, MIRType returnType, MDefinition **def) + { + if (!curBlock_) { + *def = NULL; + return true; + } + MAsmJSCall *ins = MAsmJSCall::New(callee, args.regArgs_, returnType, args.spIncrement_); + if (!ins) + return false; + curBlock_->add(ins); + *def = ins; + return true; + } + + public: + bool internalCall(const ModuleCompiler::Func &func, const Args &args, MDefinition **def) + { + MIRType returnType = func.returnType().toMIRType(); + return call(MAsmJSCall::Callee(func.codeLabel()), args, returnType, def); + } + + bool funcPtrCall(const ModuleCompiler::FuncPtrTable &funcPtrTable, MDefinition *index, + const Args &args, MDefinition **def) + { + if (!curBlock_) { + *def = NULL; + return true; + } + + MConstant *mask = MConstant::New(Int32Value(funcPtrTable.mask())); + curBlock_->add(mask); + MBitAnd *maskedIndex = MBitAnd::NewAsmJS(index, mask); + curBlock_->add(maskedIndex); + unsigned globalDataOffset = module().funcPtrIndexToGlobalDataOffset(funcPtrTable.baseIndex()); + MAsmJSLoadFuncPtr *ptrFun = MAsmJSLoadFuncPtr::New(globalDataOffset, maskedIndex); + curBlock_->add(ptrFun); + + MIRType returnType = funcPtrTable.sig().returnType().toMIRType(); + return call(MAsmJSCall::Callee(ptrFun), args, returnType, def); + } + + bool ffiCall(unsigned exitIndex, const Args &args, MIRType returnType, MDefinition **def) + { + if (!curBlock_) { + *def = NULL; + return true; + } + + JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0); + unsigned globalDataOffset = module().exitIndexToGlobalDataOffset(exitIndex); + + MAsmJSLoadFFIFunc *ptrFun = MAsmJSLoadFFIFunc::New(globalDataOffset); + curBlock_->add(ptrFun); + + return call(MAsmJSCall::Callee(ptrFun), args, returnType, def); + } + + bool builtinCall(void *builtin, const Args &args, MIRType returnType, MDefinition **def) + { + return call(MAsmJSCall::Callee(builtin), args, returnType, def); + } + + /*********************************************** Control flow generation */ + + void returnExpr(MDefinition *expr) + { + if (!curBlock_) + return; + MAsmJSReturn *ins = MAsmJSReturn::New(expr); + curBlock_->end(ins); + curBlock_ = NULL; + } + + void returnVoid() + { + if (!curBlock_) + return; + MAsmJSVoidReturn *ins = MAsmJSVoidReturn::New(); + curBlock_->end(ins); + curBlock_ = NULL; + } + + bool branchAndStartThen(MDefinition *cond, MBasicBlock **thenBlock, MBasicBlock **elseBlock) + { + if (!curBlock_) { + *thenBlock = NULL; + *elseBlock = NULL; + return true; + } + if (!newBlock(curBlock_, thenBlock) || !newBlock(curBlock_, elseBlock)) + return false; + curBlock_->end(MTest::New(cond, *thenBlock, *elseBlock)); + curBlock_ = *thenBlock; + return true; + } + + void joinIf(MBasicBlock *joinBlock) + { + if (!joinBlock) + return; + if (curBlock_) { + curBlock_->end(MGoto::New(joinBlock)); + joinBlock->addPredecessor(curBlock_); + } + curBlock_ = joinBlock; + mirGraph_.moveBlockToEnd(curBlock_); + } + + MBasicBlock *switchToElse(MBasicBlock *elseBlock) + { + if (!elseBlock) + return NULL; + MBasicBlock *thenEnd = curBlock_; + curBlock_ = elseBlock; + mirGraph_.moveBlockToEnd(curBlock_); + return thenEnd; + } + + bool joinIfElse(MBasicBlock *thenEnd) + { + if (!curBlock_ && !thenEnd) + return true; + MBasicBlock *pred = curBlock_ ? curBlock_ : thenEnd; + MBasicBlock *join; + if (!newBlock(pred, &join)) + return false; + if (curBlock_) + curBlock_->end(MGoto::New(join)); + if (thenEnd) + thenEnd->end(MGoto::New(join)); + if (curBlock_ && thenEnd) + join->addPredecessor(thenEnd); + curBlock_ = join; + return true; + } + + void pushPhiInput(MDefinition *def) + { + if (!curBlock_) + return; + JS_ASSERT(curBlock_->stackDepth() == compileInfo_.firstStackSlot()); + curBlock_->push(def); + } + + MDefinition *popPhiOutput() + { + if (!curBlock_) + return NULL; + JS_ASSERT(curBlock_->stackDepth() == compileInfo_.firstStackSlot() + 1); + return curBlock_->pop(); + } + + bool startPendingLoop(ParseNode *pn, MBasicBlock **loopEntry) + { + if (!loopStack_.append(pn) || !breakableStack_.append(pn)) + return false; + JS_ASSERT_IF(curBlock_, curBlock_->loopDepth() == loopStack_.length() - 1); + if (!curBlock_) { + *loopEntry = NULL; + return true; + } + *loopEntry = MBasicBlock::NewPendingLoopHeader(mirGraph_, compileInfo_, curBlock_, NULL); + if (!*loopEntry) + return false; + mirGraph_.addBlock(*loopEntry); + (*loopEntry)->setLoopDepth(loopStack_.length()); + curBlock_->end(MGoto::New(*loopEntry)); + curBlock_ = *loopEntry; + return true; + } + + bool branchAndStartLoopBody(MDefinition *cond, MBasicBlock **afterLoop) + { + if (!curBlock_) { + *afterLoop = NULL; + return true; + } + JS_ASSERT(curBlock_->loopDepth() > 0); + MBasicBlock *body; + if (!newBlock(curBlock_, &body)) + return false; + if (cond->isConstant() && ToBoolean(cond->toConstant()->value())) { + *afterLoop = NULL; + curBlock_->end(MGoto::New(body)); + } else { + if (!newBlockWithDepth(curBlock_, curBlock_->loopDepth() - 1, afterLoop)) + return false; + curBlock_->end(MTest::New(cond, body, *afterLoop)); + } + curBlock_ = body; + return true; + } + + private: + ParseNode *popLoop() + { + ParseNode *pn = loopStack_.back(); + JS_ASSERT(!unlabeledContinues_.has(pn)); + loopStack_.popBack(); + breakableStack_.popBack(); + return pn; + } + + public: + bool closeLoop(MBasicBlock *loopEntry, MBasicBlock *afterLoop) + { + ParseNode *pn = popLoop(); + if (!loopEntry) { + JS_ASSERT(!afterLoop); + JS_ASSERT(!curBlock_); + JS_ASSERT(!unlabeledBreaks_.has(pn)); + return true; + } + JS_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1); + JS_ASSERT_IF(afterLoop, afterLoop->loopDepth() == loopStack_.length()); + if (curBlock_) { + JS_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1); + curBlock_->end(MGoto::New(loopEntry)); + loopEntry->setBackedge(curBlock_); + } + curBlock_ = afterLoop; + if (curBlock_) + mirGraph_.moveBlockToEnd(curBlock_); + return bindUnlabeledBreaks(pn); + } + + bool branchAndCloseDoWhileLoop(MDefinition *cond, MBasicBlock *loopEntry) + { + ParseNode *pn = popLoop(); + if (!loopEntry) { + JS_ASSERT(!curBlock_); + JS_ASSERT(!unlabeledBreaks_.has(pn)); + return true; + } + JS_ASSERT(loopEntry->loopDepth() == loopStack_.length() + 1); + if (curBlock_) { + JS_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1); + if (cond->isConstant()) { + if (ToBoolean(cond->toConstant()->value())) { + curBlock_->end(MGoto::New(loopEntry)); + loopEntry->setBackedge(curBlock_); + curBlock_ = NULL; + } else { + MBasicBlock *afterLoop; + if (!newBlock(curBlock_, &afterLoop)) + return false; + curBlock_->end(MGoto::New(afterLoop)); + curBlock_ = afterLoop; + } + } else { + MBasicBlock *afterLoop; + if (!newBlock(curBlock_, &afterLoop)) + return false; + curBlock_->end(MTest::New(cond, loopEntry, afterLoop)); + loopEntry->setBackedge(curBlock_); + curBlock_ = afterLoop; + } + } + return bindUnlabeledBreaks(pn); + } + + bool bindContinues(ParseNode *pn, const LabelVector *maybeLabels) + { + if (UnlabeledBlockMap::Ptr p = unlabeledContinues_.lookup(pn)) { + if (!bindBreaksOrContinues(&p->value)) + return false; + unlabeledContinues_.remove(p); + } + return bindLabeledBreaksOrContinues(maybeLabels, &labeledContinues_); + } + + bool bindLabeledBreaks(const LabelVector *maybeLabels) + { + return bindLabeledBreaksOrContinues(maybeLabels, &labeledBreaks_); + } + + bool addBreak(PropertyName *maybeLabel) { + if (maybeLabel) + return addBreakOrContinue(maybeLabel, &labeledBreaks_); + return addBreakOrContinue(breakableStack_.back(), &unlabeledBreaks_); + } + + bool addContinue(PropertyName *maybeLabel) { + if (maybeLabel) + return addBreakOrContinue(maybeLabel, &labeledContinues_); + return addBreakOrContinue(loopStack_.back(), &unlabeledContinues_); + } + + bool startSwitch(ParseNode *pn, MDefinition *expr, int32_t low, int32_t high, + MBasicBlock **switchBlock) + { + if (!breakableStack_.append(pn)) + return false; + if (!curBlock_) { + *switchBlock = NULL; + return true; + } + curBlock_->end(MTableSwitch::New(expr, low, high)); + *switchBlock = curBlock_; + curBlock_ = NULL; + return true; + } + + bool startSwitchCase(MBasicBlock *switchBlock, MBasicBlock **next) + { + if (!switchBlock) { + *next = NULL; + return true; + } + if (!newBlock(switchBlock, next)) + return false; + if (curBlock_) { + curBlock_->end(MGoto::New(*next)); + (*next)->addPredecessor(curBlock_); + } + curBlock_ = *next; + return true; + } + + bool startSwitchDefault(MBasicBlock *switchBlock, CaseVector *cases, MBasicBlock **defaultBlock) + { + if (!startSwitchCase(switchBlock, defaultBlock)) + return false; + if (!*defaultBlock) + return true; + for (unsigned i = 0; i < cases->length(); i++) { + if (!(*cases)[i]) { + MBasicBlock *bb; + if (!newBlock(switchBlock, &bb)) + return false; + bb->end(MGoto::New(*defaultBlock)); + (*defaultBlock)->addPredecessor(bb); + (*cases)[i] = bb; + } + } + mirGraph_.moveBlockToEnd(*defaultBlock); + return true; + } + + bool joinSwitch(MBasicBlock *switchBlock, const CaseVector &cases, MBasicBlock *defaultBlock) + { + ParseNode *pn = breakableStack_.popCopy(); + if (!switchBlock) + return true; + MTableSwitch *mir = switchBlock->lastIns()->toTableSwitch(); + mir->addDefault(defaultBlock); + for (unsigned i = 0; i < cases.length(); i++) + mir->addCase(cases[i]); + if (curBlock_) { + MBasicBlock *next; + if (!newBlock(curBlock_, &next)) + return false; + curBlock_->end(MGoto::New(next)); + curBlock_ = next; + } + return bindUnlabeledBreaks(pn); + } + + /*************************************************************************/ + private: + bool newBlockWithDepth(MBasicBlock *pred, unsigned loopDepth, MBasicBlock **block) + { + *block = MBasicBlock::New(mirGraph_, compileInfo_, pred, /* pc = */ NULL, MBasicBlock::NORMAL); + if (!*block) + return false; + mirGraph_.addBlock(*block); + (*block)->setLoopDepth(loopDepth); + return true; + } + + bool newBlock(MBasicBlock *pred, MBasicBlock **block) + { + return newBlockWithDepth(pred, loopStack_.length(), block); + } + + bool bindBreaksOrContinues(BlockVector *preds) + { + for (unsigned i = 0; i < preds->length(); i++) { + MBasicBlock *pred = (*preds)[i]; + if (curBlock_ && curBlock_->begin() == curBlock_->end()) { + pred->end(MGoto::New(curBlock_)); + curBlock_->addPredecessor(pred); + } else { + MBasicBlock *next; + if (!newBlock(pred, &next)) + return false; + pred->end(MGoto::New(next)); + if (curBlock_) { + curBlock_->end(MGoto::New(next)); + next->addPredecessor(curBlock_); + } + curBlock_ = next; + } + JS_ASSERT(curBlock_->begin() == curBlock_->end()); + } + preds->clear(); + return true; + } + + bool bindLabeledBreaksOrContinues(const LabelVector *maybeLabels, LabeledBlockMap *map) + { + if (!maybeLabels) + return true; + const LabelVector &labels = *maybeLabels; + for (unsigned i = 0; i < labels.length(); i++) { + if (LabeledBlockMap::Ptr p = map->lookup(labels[i])) { + if (!bindBreaksOrContinues(&p->value)) + return false; + map->remove(p); + } + } + return true; + } + + template + bool addBreakOrContinue(Key key, Map *map) + { + if (!curBlock_) + return true; + typename Map::AddPtr p = map->lookupForAdd(key); + if (!p) { + BlockVector empty(m().cx()); + if (!map->add(p, key, Move(empty))) + return false; + } + if (!p->value.append(curBlock_)) + return false; + curBlock_ = NULL; + return true; + } + + bool bindUnlabeledBreaks(ParseNode *pn) + { + if (UnlabeledBlockMap::Ptr p = unlabeledBreaks_.lookup(pn)) { + if (!bindBreaksOrContinues(&p->value)) + return false; + unlabeledBreaks_.remove(p); + } + return true; + } +}; + +/*****************************************************************************/ +// An AsmJSModule contains the persistent results of asm.js module compilation, +// viz., the jit code and dynamic link information. +// +// An AsmJSModule object is created at the end of module compilation and +// subsequently owned by an AsmJSModuleClass JSObject. + +static void AsmJSModuleObject_finalize(FreeOp *fop, RawObject obj); +static void AsmJSModuleObject_trace(JSTracer *trc, JSRawObject obj); + +static const unsigned ASM_CODE_RESERVED_SLOT = 0; +static const unsigned ASM_CODE_NUM_RESERVED_SLOTS = 1; + +static Class AsmJSModuleClass = { + "AsmJSModuleObject", + JSCLASS_IS_ANONYMOUS | JSCLASS_IMPLEMENTS_BARRIERS | + JSCLASS_HAS_RESERVED_SLOTS(ASM_CODE_NUM_RESERVED_SLOTS), + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + NULL, /* convert */ + AsmJSModuleObject_finalize, + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* hasInstance */ + NULL, /* construct */ + AsmJSModuleObject_trace +}; + +AsmJSModule & +js::AsmJSModuleObjectToModule(JSObject *obj) +{ + JS_ASSERT(obj->getClass() == &AsmJSModuleClass); + return *(AsmJSModule *)obj->getReservedSlot(ASM_CODE_RESERVED_SLOT).toPrivate(); +} + +static void +AsmJSModuleObject_finalize(FreeOp *fop, RawObject obj) +{ + fop->delete_(&AsmJSModuleObjectToModule(obj)); +} + +static void +AsmJSModuleObject_trace(JSTracer *trc, JSRawObject obj) +{ + AsmJSModuleObjectToModule(obj).trace(trc); +} + +static JSObject * +NewAsmJSModuleObject(JSContext *cx, ScopedJSDeletePtr *module) +{ + JSObject *obj = NewObjectWithGivenProto(cx, &AsmJSModuleClass, NULL, NULL); + if (!obj) + return NULL; + + obj->setReservedSlot(ASM_CODE_RESERVED_SLOT, PrivateValue(module->forget())); + return obj; +} + +/*****************************************************************************/ +// asm.js type-checking and code-generation algorithm + +static bool +CheckIdentifier(ModuleCompiler &m, PropertyName *name, ParseNode *nameNode) +{ + if (name == m.cx()->names().arguments || name == m.cx()->names().eval) + return m.fail("disallowed asm.js parameter name", nameNode); + return true; +} + +static bool +CheckModuleLevelName(ModuleCompiler &m, PropertyName *name, ParseNode *nameNode) +{ + if (!CheckIdentifier(m, name, nameNode)) + return false; + + if (name == m.moduleFunctionName() || + name == m.globalArgumentName() || + name == m.importArgumentName() || + name == m.bufferArgumentName() || + m.lookupGlobal(name)) + { + return m.fail("Duplicate names not allowed", nameNode); + } + + return true; +} + +static bool +CheckFunctionHead(ModuleCompiler &m, ParseNode *fn, ParseNode **stmtIter) +{ + if (FunctionObject(fn)->hasRest()) + return m.fail("rest args not allowed in asm.js", fn); + if (!FunctionHasStatementList(fn)) + return m.fail("expression closures not allowed in asm.js", fn); + + *stmtIter = ListHead(FunctionStatementList(fn)); + return true; +} + +static bool +CheckArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name) +{ + if (!IsDefinition(arg)) + return m.fail("overlapping argument names not allowed", arg); + + if (MaybeDefinitionInitializer(arg)) + return m.fail("default arguments not allowed", arg); + + if (!CheckIdentifier(m, arg->name(), arg)) + return false; + + *name = arg->name(); + return true; +} + +static bool +CheckModuleArgument(ModuleCompiler &m, ParseNode *arg, PropertyName **name) +{ + if (!CheckArgument(m, arg, name)) + return false; + + if (!CheckModuleLevelName(m, *name, arg)) + return false; + + return true; +} + +static bool +CheckModuleArguments(ModuleCompiler &m, ParseNode *fn) +{ + unsigned numFormals; + ParseNode *arg1 = FunctionArgsList(fn, &numFormals); + ParseNode *arg2 = arg1 ? NextNode(arg1) : NULL; + ParseNode *arg3 = arg2 ? NextNode(arg2) : NULL; + + if (numFormals > 3) + return m.fail("asm.js modules takes at most 3 argument.", fn); + + PropertyName *arg1Name = NULL; + if (numFormals >= 1 && !CheckModuleArgument(m, arg1, &arg1Name)) + return false; + m.initGlobalArgumentName(arg1Name); + + PropertyName *arg2Name = NULL; + if (numFormals >= 2 && !CheckModuleArgument(m, arg2, &arg2Name)) + return false; + m.initImportArgumentName(arg2Name); + + PropertyName *arg3Name = NULL; + if (numFormals >= 3 && !CheckModuleArgument(m, arg3, &arg3Name)) + return false; + m.initBufferArgumentName(arg3Name); + + return true; +} + +static bool +SkipUseAsmDirective(ModuleCompiler &m, ParseNode **stmtIter) +{ + ParseNode *firstStatement = *stmtIter; + + if (!IsExpressionStatement(firstStatement)) + return m.fail("No funny stuff before the 'use asm' directive", firstStatement); + + ParseNode *expr = ExpressionStatementExpr(firstStatement); + if (!expr || !expr->isKind(PNK_STRING)) + return m.fail("No funny stuff before the 'use asm' directive", firstStatement); + + if (StringAtom(expr) != m.cx()->names().useAsm) + return m.fail("asm.js precludes other directives", firstStatement); + + *stmtIter = NextNode(firstStatement); + return true; +} + +static bool +CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode) +{ + NumLit literal = ExtractNumericLiteral(initNode); + VarType type; + switch (literal.which()) { + case NumLit::Fixnum: + case NumLit::NegativeInt: + case NumLit::BigUnsigned: + type = VarType::Int; + break; + case NumLit::Double: + type = VarType::Double; + break; + case NumLit::OutOfRangeInt: + return m.fail("Global initializer is out of representable integer range", initNode); + } + return m.addGlobalVarInitConstant(varName, type, literal.value()); +} + +static bool +CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion, + ParseNode **coercedExpr = NULL) +{ + switch (coercionNode->getKind()) { + case PNK_BITOR: { + ParseNode *rhs = BinaryRight(coercionNode); + + if (!IsNumericLiteral(rhs)) + return m.fail("Must use |0 for argument/return coercion.", rhs); + + NumLit rhsLiteral = ExtractNumericLiteral(rhs); + if (rhsLiteral.which() != NumLit::Fixnum || rhsLiteral.toInt32() != 0) + return m.fail("Must use |0 for argument/return coercion.", rhs); + + *coercion = AsmJS_ToInt32; + if (coercedExpr) + *coercedExpr = BinaryLeft(coercionNode); + return true; + } + case PNK_POS: { + *coercion = AsmJS_ToNumber; + if (coercedExpr) + *coercedExpr = UnaryKid(coercionNode); + return true; + } + default:; + } + + return m.fail("in coercion expression, the expression must be of the form +x or x|0", coercionNode); +} + +static bool +CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode) +{ + AsmJSCoercion coercion; + ParseNode *coercedExpr; + if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr)) + return false; + + if (!coercedExpr->isKind(PNK_DOT)) + return m.fail("Bad global variable import expression", coercedExpr); + + ParseNode *base = DotBase(coercedExpr); + PropertyName *field = DotMember(coercedExpr); + + if (!IsUseOfName(base, m.importArgumentName())) + return m.fail("Expecting c.y where c is the import parameter", coercedExpr); + + return m.addGlobalVarImport(varName, field, coercion); +} + +static bool +CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr, bool first) +{ + ParseNode *ctorExpr = ListHead(newExpr); + if (!ctorExpr->isKind(PNK_DOT)) + return m.fail("Only valid 'new' import is 'new global.XYZArray(buf)'", ctorExpr); + + ParseNode *base = DotBase(ctorExpr); + PropertyName *field = DotMember(ctorExpr); + + if (!IsUseOfName(base, m.globalArgumentName())) + return m.fail("Expecting global.y", base); + + ParseNode *bufArg = NextNode(ctorExpr); + if (!bufArg) + return m.fail("Constructor needs an argument", ctorExpr); + + if (NextNode(bufArg) != NULL) + return m.fail("Only one argument may be passed to a typed array constructor", bufArg); + + if (!IsUseOfName(bufArg, m.bufferArgumentName())) + return m.fail("Argument to typed array constructor must be ArrayBuffer name", bufArg); + + JSAtomState &names = m.cx()->names(); + ArrayBufferView::ViewType type; + if (field == names.Int8Array) + type = ArrayBufferView::TYPE_INT8; + else if (field == names.Uint8Array) + type = ArrayBufferView::TYPE_UINT8; + else if (field == names.Int16Array) + type = ArrayBufferView::TYPE_INT16; + else if (field == names.Uint16Array) + type = ArrayBufferView::TYPE_UINT16; + else if (field == names.Int32Array) + type = ArrayBufferView::TYPE_INT32; + else if (field == names.Uint32Array) + type = ArrayBufferView::TYPE_UINT32; + else if (field == names.Float32Array) + type = ArrayBufferView::TYPE_FLOAT32; + else if (field == names.Float64Array) + type = ArrayBufferView::TYPE_FLOAT64; + else + return m.fail("could not match typed array name", ctorExpr); + + return m.addArrayView(varName, type, field); +} + +static bool +CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode) +{ + ParseNode *base = DotBase(initNode); + PropertyName *field = DotMember(initNode); + + if (base->isKind(PNK_DOT)) { + ParseNode *global = DotBase(base); + PropertyName *math = DotMember(base); + if (!IsUseOfName(global, m.globalArgumentName()) || math != m.cx()->names().Math) + return m.fail("Expecting global.Math", base); + + AsmJSMathBuiltin mathBuiltin; + if (!m.lookupStandardLibraryMathName(field, &mathBuiltin)) + return m.fail("Does not match a standard Math builtin", initNode); + + return m.addMathBuiltin(varName, mathBuiltin, field); + } + + if (IsUseOfName(base, m.globalArgumentName())) { + if (field == m.cx()->names().NaN) + return m.addGlobalConstant(varName, js_NaN, field); + if (field == m.cx()->names().Infinity) + return m.addGlobalConstant(varName, js_PositiveInfinity, field); + return m.fail("Does not match a standard global constant", initNode); + } + + if (IsUseOfName(base, m.importArgumentName())) + return m.addFFI(varName, field); + + return m.fail("Expecting c.y where c is either the global or import parameter", initNode); +} + +static bool +CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool first) +{ + if (!IsDefinition(var)) + return m.fail("Import variable names must be unique", var); + + if (!CheckModuleLevelName(m, var->name(), var)) + return false; + + ParseNode *initNode = MaybeDefinitionInitializer(var); + if (!initNode) + return m.fail("Module import needs initializer", var); + + if (IsNumericLiteral(initNode)) + return CheckGlobalVariableInitConstant(m, var->name(), initNode); + + if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS)) + return CheckGlobalVariableInitImport(m, var->name(), initNode); + + if (initNode->isKind(PNK_NEW)) + return CheckNewArrayView(m, var->name(), initNode, first); + + if (initNode->isKind(PNK_DOT)) + return CheckGlobalDotImport(m, var->name(), initNode); + + return m.fail("Unsupported import expression", initNode); + +} + +static bool +CheckModuleGlobals(ModuleCompiler &m, ParseNode **stmtIter) +{ + ParseNode *stmt = SkipEmptyStatements(*stmtIter); + + bool first = true; + + for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) { + for (ParseNode *var = VarListHead(stmt); var; var = NextNode(var)) { + if (!CheckModuleGlobal(m, var, first)) + return false; + first = false; + } + } + + *stmtIter = stmt; + return true; +} + +static bool +CheckArgumentType(ModuleCompiler &m, ParseNode *fn, PropertyName *argName, ParseNode *stmt, + VarType *type) +{ + if (!stmt) + return m.fail("Missing parameter type declaration statements", fn); + + if (!IsExpressionStatement(stmt)) + return m.fail("Expecting expression statement type of the form 'arg = coercion;'", stmt); + + ParseNode *initNode = ExpressionStatementExpr(stmt); + if (!initNode || !initNode->isKind(PNK_ASSIGN)) + return m.fail("Expecting expression statement type of the form 'arg = coercion;'", stmt); + + ParseNode *argNode = BinaryLeft(initNode); + ParseNode *coercionNode = BinaryRight(initNode); + + if (!IsUseOfName(argNode, argName)) + return m.fail("left-hand side of 'arg = expr;' must be the name of an argument.", argNode); + + ParseNode *coercedExpr; + AsmJSCoercion coercion; + if (!CheckTypeAnnotation(m, coercionNode, &coercion, &coercedExpr)) + return false; + + if (!IsUseOfName(coercedExpr, argName)) + return m.fail("For argument type declaration, need 'x = coercion(x)'", coercedExpr); + + *type = VarType(coercion); + return true; +} + +static bool +CheckArguments(ModuleCompiler &m, ParseNode *fn, MIRTypeVector *argTypes, ParseNode **stmtIter) +{ + ParseNode *stmt = *stmtIter; + + unsigned numFormals; + ParseNode *argpn = FunctionArgsList(fn, &numFormals); + + HashSet dupSet(m.cx()); + if (!dupSet.init()) + return false; + + for (unsigned i = 0; i < numFormals; i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) { + PropertyName *argName; + if (!CheckArgument(m, argpn, &argName)) + return false; + + if (dupSet.has(argName)) + return m.fail("asm.js arguments must have distinct names", argpn); + if (!dupSet.putNew(argName)) + return false; + + VarType argType; + if (!CheckArgumentType(m, fn, argName, stmt, &argType)) + return false; + + if (!argTypes->append(argType.toMIRType())) + return false; + } + + *stmtIter = stmt; + return true; +} + +static bool +CheckReturnType(ModuleCompiler &m, ParseNode *fn, RetType *returnType) +{ + ParseNode *stmt = FunctionLastStatementOrNull(fn); + if (!stmt || !stmt->isKind(PNK_RETURN) || !UnaryKid(stmt)) { + *returnType = RetType::Void; + return true; + } + + ParseNode *coercionNode = UnaryKid(stmt); + + if (IsNumericLiteral(coercionNode)) { + switch (ExtractNumericLiteral(coercionNode).which()) { + case NumLit::BigUnsigned: + case NumLit::OutOfRangeInt: + return m.fail("Returned literal is out of integer range", coercionNode); + case NumLit::Fixnum: + case NumLit::NegativeInt: + *returnType = RetType::Signed; + break; + case NumLit::Double: + *returnType = RetType::Double; + break; + } + } else { + AsmJSCoercion coercion; + if (!CheckTypeAnnotation(m, coercionNode, &coercion)) + return false; + *returnType = RetType(coercion); + } + + JS_ASSERT(returnType->toType().isExtern()); + return true; +} + +static bool +CheckFunctionSignature(ModuleCompiler &m, ParseNode *fn) +{ + PropertyName *name = FunctionName(fn); + if (!CheckModuleLevelName(m, name, fn)) + return false; + + ParseNode *stmtIter = NULL; + + if (!CheckFunctionHead(m, fn, &stmtIter)) + return false; + + MIRTypeVector argTypes(m.cx()); + if (!CheckArguments(m, fn, &argTypes, &stmtIter)) + return false; + + RetType returnType; + if (!CheckReturnType(m, fn, &returnType)) + return false; + + ModuleCompiler::Func func(fn, stmtIter, Move(argTypes), returnType); + if (!m.addFunction(Move(func))) + return false; + + return true; +} + +static bool +CheckFunctionSignatures(ModuleCompiler &m, ParseNode **stmtIter) +{ + ParseNode *fn = SkipEmptyStatements(*stmtIter); + + for (; fn && fn->isKind(PNK_FUNCTION); fn = NextNonEmptyStatement(fn)) { + if (!CheckFunctionSignature(m, fn)) + return false; + } + + if (fn && fn->isKind(PNK_NOP)) + return m.fail("duplicate function names are not allowed", fn); + + *stmtIter = fn; + return true; +} + +static bool +SameSignature(const ModuleCompiler::Func &a, const ModuleCompiler::Func &b) +{ + if (a.numArgs() != b.numArgs() || a.returnType() != b.returnType()) + return false; + for (unsigned i = 0; i < a.numArgs(); i++) { + if (a.argType(i) != b.argType(i)) + return false; + } + return true; +} + +static bool +CheckFuncPtrTable(ModuleCompiler &m, ParseNode *var) +{ + if (!IsDefinition(var)) + return m.fail("Function-pointer table name must be unique", var); + + PropertyName *name = var->name(); + + if (!CheckModuleLevelName(m, name, var)) + return false; + + ParseNode *arrayLiteral = MaybeDefinitionInitializer(var); + if (!arrayLiteral || !arrayLiteral->isKind(PNK_ARRAY)) + return m.fail("Function-pointer table's initializer must be an array literal", var); + + unsigned length = ListLength(arrayLiteral); + + if (!IsPowerOfTwo(length)) + return m.fail("Function-pointer table's length must be a power of 2", arrayLiteral); + + ModuleCompiler::FuncPtrVector funcPtrs(m.cx()); + const ModuleCompiler::Func *firstFunction = NULL; + + for (ParseNode *elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) { + if (!elem->isKind(PNK_NAME)) + return m.fail("Function-pointer table's elements must be names of functions", elem); + + PropertyName *funcName = elem->name(); + const ModuleCompiler::Func *func = m.lookupFunction(funcName); + if (!func) + return m.fail("Function-pointer table's elements must be names of functions", elem); + + if (firstFunction) { + if (!SameSignature(*firstFunction, *func)) + return m.fail("All functions in table must have same signature", elem); + } else { + firstFunction = func; + } + + if (!funcPtrs.append(func)) + return false; + } + + return m.addFuncPtrTable(name, Move(funcPtrs)); +} + +static bool +CheckFuncPtrTables(ModuleCompiler &m, ParseNode **stmtIter) +{ + ParseNode *stmt = SkipEmptyStatements(*stmtIter); + + for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) { + for (ParseNode *var = VarListHead(stmt); var; var = NextNode(var)) { + if (!CheckFuncPtrTable(m, var)) + return false; + } + } + + *stmtIter = stmt; + return true; +} + +static bool +CheckModuleExportFunction(ModuleCompiler &m, ParseNode *returnExpr) +{ + if (!returnExpr->isKind(PNK_NAME)) + return m.fail("an asm.js export statement must be of the form 'return name'", returnExpr); + + PropertyName *funcName = returnExpr->name(); + + const ModuleCompiler::Func *func = m.lookupFunction(funcName); + if (!func) + return m.fail("exported function name not found", returnExpr); + + return m.addExportedFunction(func, /* maybeFieldName = */ NULL); +} + +static bool +CheckModuleExportObject(ModuleCompiler &m, ParseNode *object) +{ + JS_ASSERT(object->isKind(PNK_OBJECT)); + + for (ParseNode *pn = ListHead(object); pn; pn = NextNode(pn)) { + if (!IsNormalObjectField(m.cx(), pn)) + return m.fail("Only normal object properties may be used in the export object literal", pn); + + PropertyName *fieldName = ObjectNormalFieldName(m.cx(), pn); + + ParseNode *initNode = ObjectFieldInitializer(pn); + if (!initNode->isKind(PNK_NAME)) + return m.fail("Initializer of exported object literal must be name of function", initNode); + + PropertyName *funcName = initNode->name(); + + const ModuleCompiler::Func *func = m.lookupFunction(funcName); + if (!func) + return m.fail("exported function name not found", initNode); + + if (!m.addExportedFunction(func, fieldName)) + return false; + } + + return true; +} + +static bool +CheckModuleExports(ModuleCompiler &m, ParseNode *fn, ParseNode **stmtIter) +{ + ParseNode *returnNode = SkipEmptyStatements(*stmtIter); + + if (!returnNode || !returnNode->isKind(PNK_RETURN)) + return m.fail("asm.js must end with a return export statement", fn); + + ParseNode *returnExpr = UnaryKid(returnNode); + + if (!returnExpr) + return m.fail("an asm.js export statement must return something", returnNode); + + if (returnExpr->isKind(PNK_OBJECT)) { + if (!CheckModuleExportObject(m, returnExpr)) + return false; + } else { + if (!CheckModuleExportFunction(m, returnExpr)) + return false; + } + + *stmtIter = NextNonEmptyStatement(returnNode); + return true; +} + +static bool +CheckExpr(FunctionCompiler &f, ParseNode *expr, Use use, MDefinition **def, Type *type); + +static bool +CheckNumericLiteral(FunctionCompiler &f, ParseNode *num, MDefinition **def, Type *type) +{ + JS_ASSERT(IsNumericLiteral(num)); + NumLit literal = ExtractNumericLiteral(num); + + switch (literal.which()) { + case NumLit::Fixnum: + case NumLit::NegativeInt: + case NumLit::BigUnsigned: + case NumLit::Double: + break; + case NumLit::OutOfRangeInt: + return f.fail("Numeric literal out of representable integer range", num); + } + + *type = literal.type(); + *def = f.constant(literal.value()); + return true; +} + +static bool +CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *type) +{ + PropertyName *name = varRef->name(); + + if (const FunctionCompiler::Local *local = f.lookupLocal(name)) { + *def = f.getLocalDef(*local); + *type = local->type.toType(); + return true; + } + + if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) { + switch (global->which()) { + case ModuleCompiler::Global::Constant: + *def = f.constant(DoubleValue(global->constant())); + *type = Type::Double; + break; + case ModuleCompiler::Global::Variable: + *def = f.loadGlobalVar(*global); + *type = global->varType().toType(); + break; + case ModuleCompiler::Global::Function: + case ModuleCompiler::Global::FFI: + case ModuleCompiler::Global::MathBuiltin: + case ModuleCompiler::Global::FuncPtrTable: + case ModuleCompiler::Global::ArrayView: + return f.fail("Global may not be accessed by ordinary expressions", varRef); + } + return true; + } + + return f.fail("Name not found in scope", varRef); +} + +static bool +CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType, + MDefinition **def) +{ + ParseNode *viewName = ElemBase(elem); + ParseNode *indexExpr = ElemIndex(elem); + + if (!viewName->isKind(PNK_NAME)) + return f.fail("Left-hand side of x[y] must be a name", viewName); + + const ModuleCompiler::Global *global = f.lookupGlobal(viewName->name()); + if (!global || global->which() != ModuleCompiler::Global::ArrayView) + return f.fail("Left-hand side of x[y] must be typed array view name", viewName); + + *viewType = global->viewType(); + + uint32_t pointer; + if (IsLiteralUint32(indexExpr, &pointer)) { + pointer <<= TypedArrayShift(*viewType); + *def = f.constant(Int32Value(pointer)); + return true; + } + + MDefinition *pointerDef; + if (indexExpr->isKind(PNK_RSH)) { + ParseNode *shiftNode = BinaryRight(indexExpr); + ParseNode *pointerNode = BinaryLeft(indexExpr); + + uint32_t shift; + if (!IsLiteralUint32(shiftNode, &shift) || shift != TypedArrayShift(*viewType)) + return f.fail("The shift amount must be a constant matching the array " + "element size", shiftNode); + + Type pointerType; + if (!CheckExpr(f, pointerNode, Use::ToInt32, &pointerDef, &pointerType)) + return false; + + if (!pointerType.isIntish()) + return f.fail("Pointer input must be intish", pointerNode); + } else { + if (TypedArrayShift(*viewType) != 0) + return f.fail("The shift amount is 0 so this must be a Int8/Uint8 array", indexExpr); + + Type pointerType; + if (!CheckExpr(f, indexExpr, Use::ToInt32, &pointerDef, &pointerType)) + return false; + + if (!pointerType.isInt()) + return f.fail("Pointer input must be int", indexExpr); + } + + // Mask off the low bits to account for clearing effect of a right shift + // followed by the left shift implicit in the array access. E.g., H32[i>>2] + // loses the low two bits. + int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1); + *def = f.bitwise(pointerDef, f.constant(Int32Value(mask))); + return true; +} + +static bool +CheckArrayLoad(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type) +{ + ArrayBufferView::ViewType viewType; + MDefinition *pointerDef; + if (!CheckArrayAccess(f, elem, &viewType, &pointerDef)) + return false; + + *def = f.loadHeap(viewType, pointerDef); + *type = TypedArrayLoadType(viewType); + return true; +} + +static bool +CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type) +{ + ArrayBufferView::ViewType viewType; + MDefinition *pointerDef; + if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef)) + return false; + + MDefinition *rhsDef; + Type rhsType; + if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType)) + return false; + + switch (TypedArrayStoreType(viewType)) { + case ArrayStore_Intish: + if (!rhsType.isIntish()) + return f.fail("Right-hand side of store must be intish", lhs); + break; + case ArrayStore_Double: + if (rhsType != Type::Double) + return f.fail("Right-hand side of store must be double", lhs); + break; + } + + f.storeHeap(viewType, pointerDef, rhsDef); + + *def = rhsDef; + *type = rhsType; + return true; +} + +static bool +CheckAssignName(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type) +{ + PropertyName *name = lhs->name(); + + MDefinition *rhsDef; + Type rhsType; + if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType)) + return false; + + if (const FunctionCompiler::Local *lhsVar = f.lookupLocal(name)) { + if (!(rhsType <= lhsVar->type)) + return f.fail("Right-hand side of assignment must be subtype of left-hand side", lhs); + f.assign(*lhsVar, rhsDef); + } else if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) { + if (global->which() != ModuleCompiler::Global::Variable) + return f.fail("Only global variables are mutable, not FFI functions etc", lhs); + if (!(rhsType <= global->varType())) + return f.fail("Right-hand side of assignment must be subtype of left-hand side", lhs); + f.storeGlobalVar(*global, rhsDef); + } else { + return f.fail("Variable name in left-hand side of assignment not found", lhs); + } + + *def = rhsDef; + *type = rhsType; + return true; +} + +static bool +CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *type) +{ + JS_ASSERT(assign->isKind(PNK_ASSIGN)); + ParseNode *lhs = BinaryLeft(assign); + ParseNode *rhs = BinaryRight(assign); + + if (lhs->getKind() == PNK_ELEM) + return CheckStoreArray(f, lhs, rhs, def, type); + + if (lhs->getKind() == PNK_NAME) + return CheckAssignName(f, lhs, rhs, def, type); + + return f.fail("Left-hand side of assignment must be a variable or heap", assign); +} + +static bool +CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type) +{ + if (CallArgListLength(call) != 2) + return f.fail("Math.imul must be passed 2 arguments", call); + + ParseNode *lhs = CallArgList(call); + ParseNode *rhs = NextNode(lhs); + + MDefinition *lhsDef; + Type lhsType; + if (!CheckExpr(f, lhs, Use::ToInt32, &lhsDef, &lhsType)) + return false; + + MDefinition *rhsDef; + Type rhsType; + if (!CheckExpr(f, rhs, Use::ToInt32, &rhsDef, &rhsType)) + return false; + + if (!lhsType.isIntish() || !rhsType.isIntish()) + return f.fail("Math.imul calls must be passed 2 intish arguments", call); + + *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer); + *type = Type::Signed; + return true; +} + +static bool +CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type) +{ + if (CallArgListLength(call) != 1) + return f.fail("Math.abs must be passed 1 argument", call); + + ParseNode *arg = CallArgList(call); + + MDefinition *argDef; + Type argType; + if (!CheckExpr(f, arg, Use::ToNumber, &argDef, &argType)) + return false; + + if (argType.isSigned()) { + *def = f.unary(argDef, MIRType_Int32); + *type = Type::Unsigned; + return true; + } + + if (argType.isDoublish()) { + *def = f.unary(argDef, MIRType_Double); + *type = Type::Double; + return true; + } + + return f.fail("Math.abs must be passed either a signed or doublish argument", call); +} + +static bool +CheckCallArgs(FunctionCompiler &f, ParseNode *callNode, Use use, FunctionCompiler::Args *args) +{ + f.startCallArgs(args); + + ParseNode *argNode = CallArgList(callNode); + for (unsigned i = 0; i < CallArgListLength(callNode); i++, argNode = NextNode(argNode)) { + MDefinition *argDef; + Type argType; + if (!CheckExpr(f, argNode, use, &argDef, &argType)) + return false; + + if (argType.isVoid()) + return f.fail("Void cannot be passed as an argument", argNode); + + if (!f.passArg(argDef, argType, args)) + return false; + } + + f.finishCallArgs(args); + return true; +} + +static bool +CheckInternalCall(FunctionCompiler &f, ParseNode *callNode, const ModuleCompiler::Func &callee, + MDefinition **def, Type *type) +{ + FunctionCompiler::Args args(f); + + if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args)) + return false; + + if (args.length() != callee.numArgs()) + return f.fail("Wrong number of arguments", callNode); + + for (unsigned i = 0; i < args.length(); i++) { + if (!(args.type(i) <= callee.argType(i))) + return f.fail("actual arg type is not subtype of formal arg type", callNode); + } + + if (!f.internalCall(callee, args, def)) + return false; + + *type = callee.returnType().toType(); + return true; +} + +static bool +CheckFuncPtrCall(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, Type *type) +{ + ParseNode *callee = CallCallee(callNode); + ParseNode *elemBase = ElemBase(callee); + ParseNode *indexExpr = ElemIndex(callee); + + if (!elemBase->isKind(PNK_NAME)) + return f.fail("Expecting name (of function-pointer array)", elemBase); + + const ModuleCompiler::FuncPtrTable *table = f.m().lookupFuncPtrTable(elemBase->name()); + if (!table) + return f.fail("Expecting name of function-pointer array", elemBase); + + if (!indexExpr->isKind(PNK_BITAND)) + return f.fail("Function-pointer table index expression needs & mask", indexExpr); + + ParseNode *indexNode = BinaryLeft(indexExpr); + ParseNode *maskNode = BinaryRight(indexExpr); + + uint32_t mask; + if (!IsLiteralUint32(maskNode, &mask) || mask != table->mask()) + return f.fail("Function-pointer table index mask must be the table length minus 1", maskNode); + + MDefinition *indexDef; + Type indexType; + if (!CheckExpr(f, indexNode, Use::ToInt32, &indexDef, &indexType)) + return false; + + if (!indexType.isIntish()) + return f.fail("Function-pointer table index expression must be intish", indexNode); + + FunctionCompiler::Args args(f); + + if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args)) + return false; + + if (args.length() != table->sig().numArgs()) + return f.fail("Wrong number of arguments", callNode); + + for (unsigned i = 0; i < args.length(); i++) { + if (!(args.type(i) <= table->sig().argType(i))) + return f.fail("actual arg type is not subtype of formal arg type", callNode); + } + + if (!f.funcPtrCall(*table, indexDef, args, def)) + return false; + + *type = table->sig().returnType().toType(); + return true; +} + +static bool +CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, Use use, + MDefinition **def, Type *type) +{ + FunctionCompiler::Args args(f); + + if (!CheckCallArgs(f, callNode, Use::NoCoercion, &args)) + return false; + + MIRTypeVector argMIRTypes(f.cx()); + for (unsigned i = 0; i < args.length(); i++) { + if (!args.type(i).isExtern()) + return f.fail("args to FFI call must be <: extern", callNode); + if (!argMIRTypes.append(args.type(i).toMIRType())) + return false; + } + + unsigned exitIndex; + if (!f.m().addExit(ffiIndex, CallCallee(callNode)->name(), Move(argMIRTypes), use, &exitIndex)) + return false; + + if (!f.ffiCall(exitIndex, args, use.toMIRType(), def)) + return false; + + *type = use.toFFIReturnType(); + return true; +} + +static inline void * +UnaryMathFunCast(double (*pf)(double)) +{ + return JS_FUNC_TO_DATA_PTR(void*, pf); +} + +static inline void * +BinaryMathFunCast(double (*pf)(double, double)) +{ + return JS_FUNC_TO_DATA_PTR(void*, pf); +} + +static bool +CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin mathBuiltin, + MDefinition **def, Type *type) +{ + unsigned arity = 0; + void *callee = NULL; + switch (mathBuiltin) { + case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, def, type); + case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, def, type); + case AsmJSMathBuiltin_sin: arity = 1; callee = UnaryMathFunCast(sin); break; + case AsmJSMathBuiltin_cos: arity = 1; callee = UnaryMathFunCast(cos); break; + case AsmJSMathBuiltin_tan: arity = 1; callee = UnaryMathFunCast(tan); break; + case AsmJSMathBuiltin_asin: arity = 1; callee = UnaryMathFunCast(asin); break; + case AsmJSMathBuiltin_acos: arity = 1; callee = UnaryMathFunCast(acos); break; + case AsmJSMathBuiltin_atan: arity = 1; callee = UnaryMathFunCast(atan); break; + case AsmJSMathBuiltin_ceil: arity = 1; callee = UnaryMathFunCast(ceil); break; + case AsmJSMathBuiltin_floor: arity = 1; callee = UnaryMathFunCast(floor); break; + case AsmJSMathBuiltin_exp: arity = 1; callee = UnaryMathFunCast(exp); break; + case AsmJSMathBuiltin_log: arity = 1; callee = UnaryMathFunCast(log); break; + case AsmJSMathBuiltin_sqrt: arity = 1; callee = UnaryMathFunCast(sqrt); break; + case AsmJSMathBuiltin_pow: arity = 2; callee = BinaryMathFunCast(ecmaPow); break; + case AsmJSMathBuiltin_atan2: arity = 2; callee = BinaryMathFunCast(ecmaAtan2); break; + } + + FunctionCompiler::Args args(f); + + if (!CheckCallArgs(f, callNode, Use::ToNumber, &args)) + return false; + + if (args.length() != arity) + return f.fail("Math builtin call passed wrong number of argument", callNode); + + for (unsigned i = 0; i < args.length(); i++) { + if (!args.type(i).isDoublish()) + return f.fail("Builtin calls must be passed 1 doublish argument", callNode); + } + + if (!f.builtinCall(callee, args, MIRType_Double, def)) + return false; + + *type = Type::Double; + return true; +} + +static bool +CheckCall(FunctionCompiler &f, ParseNode *call, Use use, MDefinition **def, Type *type) +{ + ParseNode *callee = CallCallee(call); + + if (callee->isKind(PNK_ELEM)) + return CheckFuncPtrCall(f, call, def, type); + + if (!callee->isKind(PNK_NAME)) + return f.fail("Unexpected callee expression type", callee); + + if (const ModuleCompiler::Global *global = f.lookupGlobal(callee->name())) { + switch (global->which()) { + case ModuleCompiler::Global::Function: + return CheckInternalCall(f, call, f.m().function(global->funcIndex()), def, type); + case ModuleCompiler::Global::FFI: + return CheckFFICall(f, call, global->ffiIndex(), use, def, type); + case ModuleCompiler::Global::MathBuiltin: + return CheckMathBuiltinCall(f, call, global->mathBuiltin(), def, type); + case ModuleCompiler::Global::Constant: + case ModuleCompiler::Global::Variable: + case ModuleCompiler::Global::FuncPtrTable: + case ModuleCompiler::Global::ArrayView: + return f.fail("Global is not callable function", callee); + } + } + + return f.fail("Call target not found in global scope", callee); +} + +static bool +CheckPos(FunctionCompiler &f, ParseNode *pos, MDefinition **def, Type *type) +{ + JS_ASSERT(pos->isKind(PNK_POS)); + ParseNode *operand = UnaryKid(pos); + + MDefinition *operandDef; + Type operandType; + if (!CheckExpr(f, operand, Use::ToNumber, &operandDef, &operandType)) + return false; + + if (operandType.isSigned()) + *def = f.unary(operandDef); + else if (operandType.isUnsigned()) + *def = f.unary(operandDef); + else if (operandType.isDoublish()) + *def = operandDef; + else + return f.fail("Operand to unary + must be signed, unsigned or doubleish", operand); + + *type = Type::Double; + return true; +} + +static bool +CheckNot(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) +{ + JS_ASSERT(expr->isKind(PNK_NOT)); + ParseNode *operand = UnaryKid(expr); + + MDefinition *operandDef; + Type operandType; + if (!CheckExpr(f, operand, Use::NoCoercion, &operandDef, &operandType)) + return false; + + if (!operandType.isInt()) + return f.fail("Operand to ! must be int", operand); + + *def = f.unary(operandDef); + *type = Type::Int; + return true; +} + +static bool +CheckNeg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) +{ + JS_ASSERT(expr->isKind(PNK_NEG)); + ParseNode *operand = UnaryKid(expr); + + MDefinition *operandDef; + Type operandType; + if (!CheckExpr(f, operand, Use::ToNumber, &operandDef, &operandType)) + return false; + + if (operandType.isInt()) { + *def = f.unary(operandDef, MIRType_Int32); + *type = Type::Intish; + return true; + } + + if (operandType.isDoublish()) { + *def = f.unary(operandDef, MIRType_Double); + *type = Type::Double; + return true; + } + + return f.fail("Operand to unary - must be an int", operand); +} + +static bool +CheckBitNot(FunctionCompiler &f, ParseNode *neg, MDefinition **def, Type *type) +{ + JS_ASSERT(neg->isKind(PNK_BITNOT)); + ParseNode *operand = UnaryKid(neg); + + if (operand->isKind(PNK_BITNOT)) { + MDefinition *operandDef; + Type operandType; + if (!CheckExpr(f, UnaryKid(operand), Use::NoCoercion, &operandDef, &operandType)) + return false; + + if (operandType.isDouble()) { + *def = f.unary(operandDef); + *type = Type::Signed; + return true; + } + } + + MDefinition *operandDef; + Type operandType; + if (!CheckExpr(f, operand, Use::ToInt32, &operandDef, &operandType)) + return false; + + if (!operandType.isIntish()) + return f.fail("Operand to ~ must be intish", operand); + + *def = f.bitwise(operandDef); + *type = Type::Signed; + return true; +} + +static bool +CheckComma(FunctionCompiler &f, ParseNode *comma, Use use, MDefinition **def, Type *type) +{ + JS_ASSERT(comma->isKind(PNK_COMMA)); + ParseNode *operands = ListHead(comma); + + ParseNode *pn = operands; + for (; NextNode(pn); pn = NextNode(pn)) { + if (!CheckExpr(f, pn, Use::NoCoercion, def, type)) + return false; + } + + if (!CheckExpr(f, pn, use, def, type)) + return false; + + return true; +} + +static bool +CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Type *type) +{ + JS_ASSERT(ternary->isKind(PNK_CONDITIONAL)); + ParseNode *cond = TernaryKid1(ternary); + ParseNode *thenExpr = TernaryKid2(ternary); + ParseNode *elseExpr = TernaryKid3(ternary); + + MDefinition *condDef; + Type condType; + if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) + return false; + + if (!condType.isInt()) + return f.fail("Condition of if must be boolish", cond); + + MBasicBlock *thenBlock, *elseBlock; + if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock)) + return false; + + MDefinition *thenDef; + Type thenType; + if (!CheckExpr(f, thenExpr, Use::NoCoercion, &thenDef, &thenType)) + return false; + + f.pushPhiInput(thenDef); + MBasicBlock *thenEnd = f.switchToElse(elseBlock); + + MDefinition *elseDef; + Type elseType; + if (!CheckExpr(f, elseExpr, Use::NoCoercion, &elseDef, &elseType)) + return false; + + f.pushPhiInput(elseDef); + if (!f.joinIfElse(thenEnd)) + return false; + *def = f.popPhiOutput(); + + if (thenType.isInt() && elseType.isInt()) + *type = Type::Int; + else if (thenType.isDouble() && elseType.isDouble()) + *type = Type::Double; + else + return f.fail("Then/else branches of conditional must both be int or double", ternary); + + return true; +} + +static bool +IsValidIntMultiplyConstant(ParseNode *expr) +{ + if (!IsNumericLiteral(expr)) + return false; + + NumLit literal = ExtractNumericLiteral(expr); + switch (literal.which()) { + case NumLit::Fixnum: + case NumLit::NegativeInt: + if (abs(literal.toInt32()) < (1<<20)) + return true; + return false; + case NumLit::BigUnsigned: + case NumLit::Double: + case NumLit::OutOfRangeInt: + return false; + } + + JS_NOT_REACHED("Bad literal"); + return false; +} + +static bool +CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type) +{ + JS_ASSERT(star->isKind(PNK_STAR)); + ParseNode *lhs = BinaryLeft(star); + ParseNode *rhs = BinaryRight(star); + + MDefinition *lhsDef; + Type lhsType; + if (!CheckExpr(f, lhs, Use::ToNumber, &lhsDef, &lhsType)) + return false; + + MDefinition *rhsDef; + Type rhsType; + if (!CheckExpr(f, rhs, Use::ToNumber, &rhsDef, &rhsType)) + return false; + + if (lhsType.isInt() && rhsType.isInt()) { + if (!IsValidIntMultiplyConstant(lhs) && !IsValidIntMultiplyConstant(rhs)) + return f.fail("One arg to int multiply must be small (-2^20, 2^20) int literal", star); + *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer); + *type = Type::Signed; + return true; + } + + if (lhsType.isDoublish() && rhsType.isDoublish()) { + *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal); + *type = Type::Double; + return true; + } + + return f.fail("Arguments to * must both be doubles", star); +} + +static bool +CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, Use use, MDefinition **def, Type *type) +{ + JS_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB)); + ParseNode *lhs = BinaryLeft(expr); + ParseNode *rhs = BinaryRight(expr); + + Use argUse; + unsigned addOrSubCount = 1; + if (use.which() == Use::AddOrSub) { + if (++use.addOrSubCount() > (1<<20)) + return f.fail("Too many + or - without intervening coercion", expr); + argUse = use; + } else { + argUse = Use(&addOrSubCount); + } + + MDefinition *lhsDef, *rhsDef; + Type lhsType, rhsType; + if (!CheckExpr(f, lhs, argUse, &lhsDef, &lhsType)) + return false; + if (!CheckExpr(f, rhs, argUse, &rhsDef, &rhsType)) + return false; + + if (lhsType.isInt() && rhsType.isInt()) { + *def = expr->isKind(PNK_ADD) + ? f.binary(lhsDef, rhsDef, MIRType_Int32) + : f.binary(lhsDef, rhsDef, MIRType_Int32); + if (use.which() == Use::AddOrSub) + *type = Type::Int; + else + *type = Type::Intish; + return true; + } + + if (expr->isKind(PNK_ADD) && lhsType.isDouble() && rhsType.isDouble()) { + *def = f.binary(lhsDef, rhsDef, MIRType_Double); + *type = Type::Double; + return true; + } + + if (expr->isKind(PNK_SUB) && lhsType.isDoublish() && rhsType.isDoublish()) { + *def = f.binary(lhsDef, rhsDef, MIRType_Double); + *type = Type::Double; + return true; + } + + return f.fail("Arguments to + or - must both be ints or doubles", expr); +} + +static bool +CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) +{ + JS_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD)); + ParseNode *lhs = BinaryLeft(expr); + ParseNode *rhs = BinaryRight(expr); + + MDefinition *lhsDef, *rhsDef; + Type lhsType, rhsType; + if (!CheckExpr(f, lhs, Use::ToNumber, &lhsDef, &lhsType)) + return false; + if (!CheckExpr(f, rhs, Use::ToNumber, &rhsDef, &rhsType)) + return false; + + if (lhsType.isDoublish() && rhsType.isDoublish()) { + *def = expr->isKind(PNK_DIV) + ? f.binary(lhsDef, rhsDef, MIRType_Double) + : f.binary(lhsDef, rhsDef, MIRType_Double); + *type = Type::Double; + return true; + } + + if (lhsType.isSigned() && rhsType.isSigned()) { + if (expr->isKind(PNK_DIV)) { + *def = f.binary(lhsDef, rhsDef, MIRType_Int32); + *type = Type::Intish; + } else { + *def = f.binary(lhsDef, rhsDef, MIRType_Int32); + *type = Type::Int; + } + return true; + } + + if (lhsType.isUnsigned() && rhsType.isUnsigned()) { + if (expr->isKind(PNK_DIV)) { + *def = f.binary(lhsDef, rhsDef); + *type = Type::Intish; + } else { + *def = f.binary(lhsDef, rhsDef); + *type = Type::Int; + } + return true; + } + + return f.fail("Arguments to / or % must both be double, signed, or unsigned", expr); +} + +static bool +CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type) +{ + JS_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) || + comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE)); + ParseNode *lhs = BinaryLeft(comp); + ParseNode *rhs = BinaryRight(comp); + + MDefinition *lhsDef, *rhsDef; + Type lhsType, rhsType; + if (!CheckExpr(f, lhs, Use::NoCoercion, &lhsDef, &lhsType)) + return false; + if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType)) + return false; + + if ((lhsType.isSigned() && rhsType.isSigned()) || (lhsType.isUnsigned() && rhsType.isUnsigned())) { + MCompare::CompareType compareType = lhsType.isUnsigned() + ? MCompare::Compare_UInt32 + : MCompare::Compare_Int32; + *def = f.compare(lhsDef, rhsDef, comp->getOp(), compareType); + *type = Type::Int; + return true; + } + + if (lhsType.isDouble() && rhsType.isDouble()) { + *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Double); + *type = Type::Int; + return true; + } + + return f.fail("The arguments to a comparison must both be signed, unsigned or doubles", comp); +} + +static bool +CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type) +{ + ParseNode *lhs = BinaryLeft(bitwise); + ParseNode *rhs = BinaryRight(bitwise); + + int32_t identityElement; + bool onlyOnRight; + switch (bitwise->getKind()) { + case PNK_BITOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break; + case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed; break; + case PNK_BITXOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break; + case PNK_LSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break; + case PNK_RSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break; + case PNK_URSH: identityElement = 0; onlyOnRight = true; *type = Type::Unsigned; break; + default: JS_NOT_REACHED("not a bitwise op"); + } + + if (!onlyOnRight && IsBits32(lhs, identityElement)) { + Type rhsType; + if (!CheckExpr(f, rhs, Use::ToInt32, def, &rhsType)) + return false; + if (!rhsType.isIntish()) + return f.fail("Operands to bitwise ops must be intish", bitwise); + return true; + } + + if (IsBits32(rhs, identityElement)) { + Type lhsType; + if (!CheckExpr(f, lhs, Use::ToInt32, def, &lhsType)) + return false; + if (!lhsType.isIntish()) + return f.fail("Operands to bitwise ops must be intish", bitwise); + return true; + } + + MDefinition *lhsDef; + Type lhsType; + if (!CheckExpr(f, lhs, Use::ToInt32, &lhsDef, &lhsType)) + return false; + + MDefinition *rhsDef; + Type rhsType; + if (!CheckExpr(f, rhs, Use::ToInt32, &rhsDef, &rhsType)) + return false; + + if (!lhsType.isIntish() || !rhsType.isIntish()) + return f.fail("Operands to bitwise ops must be intish", bitwise); + + switch (bitwise->getKind()) { + case PNK_BITOR: *def = f.bitwise(lhsDef, rhsDef); break; + case PNK_BITAND: *def = f.bitwise(lhsDef, rhsDef); break; + case PNK_BITXOR: *def = f.bitwise(lhsDef, rhsDef); break; + case PNK_LSH: *def = f.bitwise(lhsDef, rhsDef); break; + case PNK_RSH: *def = f.bitwise(lhsDef, rhsDef); break; + case PNK_URSH: *def = f.bitwise(lhsDef, rhsDef); break; + default: JS_NOT_REACHED("not a bitwise op"); + } + + return true; +} + +static bool +CheckExpr(FunctionCompiler &f, ParseNode *expr, Use use, MDefinition **def, Type *type) +{ + JS_CHECK_RECURSION(f.cx(), return false); + + if (!f.m().alloc().ensureBallast()) + return false; + + if (IsNumericLiteral(expr)) + return CheckNumericLiteral(f, expr, def, type); + + switch (expr->getKind()) { + case PNK_NAME: return CheckVarRef(f, expr, def, type); + case PNK_ELEM: return CheckArrayLoad(f, expr, def, type); + case PNK_ASSIGN: return CheckAssign(f, expr, def, type); + case PNK_CALL: return CheckCall(f, expr, use, def, type); + case PNK_POS: return CheckPos(f, expr, def, type); + case PNK_NOT: return CheckNot(f, expr, def, type); + case PNK_NEG: return CheckNeg(f, expr, def, type); + case PNK_BITNOT: return CheckBitNot(f, expr, def, type); + case PNK_COMMA: return CheckComma(f, expr, use, def, type); + case PNK_CONDITIONAL: return CheckConditional(f, expr, def, type); + + case PNK_STAR: return CheckMultiply(f, expr, def, type); + + case PNK_ADD: + case PNK_SUB: return CheckAddOrSub(f, expr, use, def, type); + + case PNK_DIV: + case PNK_MOD: return CheckDivOrMod(f, expr, def, type); + + case PNK_LT: + case PNK_LE: + case PNK_GT: + case PNK_GE: + case PNK_EQ: + case PNK_NE: return CheckComparison(f, expr, def, type); + + case PNK_BITOR: + case PNK_BITAND: + case PNK_BITXOR: + case PNK_LSH: + case PNK_RSH: + case PNK_URSH: return CheckBitwise(f, expr, def, type); + + default:; + } + + return f.fail("Unsupported expression", expr); +} + +static bool +CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels = NULL); + +static bool +CheckExprStatement(FunctionCompiler &f, ParseNode *exprStmt) +{ + JS_ASSERT(exprStmt->isKind(PNK_SEMI)); + ParseNode *expr = UnaryKid(exprStmt); + + if (!expr) + return true; + + MDefinition *_1; + Type _2; + if (!CheckExpr(f, UnaryKid(exprStmt), Use::NoCoercion, &_1, &_2)) + return false; + + return true; +} + +static bool +CheckWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLabels) +{ + JS_ASSERT(whileStmt->isKind(PNK_WHILE)); + ParseNode *cond = BinaryLeft(whileStmt); + ParseNode *body = BinaryRight(whileStmt); + + MBasicBlock *loopEntry; + if (!f.startPendingLoop(whileStmt, &loopEntry)) + return false; + + MDefinition *condDef; + Type condType; + if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) + return false; + + if (!condType.isInt()) + return f.fail("Condition of while loop must be boolish", cond); + + MBasicBlock *afterLoop; + if (!f.branchAndStartLoopBody(condDef, &afterLoop)) + return false; + + if (!CheckStatement(f, body)) + return false; + + if (!f.bindContinues(whileStmt, maybeLabels)) + return false; + + return f.closeLoop(loopEntry, afterLoop); +} + +static bool +CheckFor(FunctionCompiler &f, ParseNode *forStmt, const LabelVector *maybeLabels) +{ + JS_ASSERT(forStmt->isKind(PNK_FOR)); + ParseNode *forHead = BinaryLeft(forStmt); + ParseNode *body = BinaryRight(forStmt); + + if (!forHead->isKind(PNK_FORHEAD)) + return f.fail("Unsupported for-loop statement", forHead); + + ParseNode *maybeInit = TernaryKid1(forHead); + ParseNode *maybeCond = TernaryKid2(forHead); + ParseNode *maybeInc = TernaryKid3(forHead); + + if (maybeInit) { + MDefinition *_1; + Type _2; + if (!CheckExpr(f, maybeInit, Use::NoCoercion, &_1, &_2)) + return false; + } + + MBasicBlock *loopEntry; + if (!f.startPendingLoop(forStmt, &loopEntry)) + return false; + + MDefinition *condDef; + if (maybeCond) { + Type condType; + if (!CheckExpr(f, maybeCond, Use::NoCoercion, &condDef, &condType)) + return false; + + if (!condType.isInt()) + return f.fail("Condition of while loop must be boolish", maybeCond); + } else { + condDef = f.constant(Int32Value(1)); + } + + MBasicBlock *afterLoop; + if (!f.branchAndStartLoopBody(condDef, &afterLoop)) + return false; + + if (!CheckStatement(f, body)) + return false; + + if (!f.bindContinues(forStmt, maybeLabels)) + return false; + + if (maybeInc) { + MDefinition *_1; + Type _2; + if (!CheckExpr(f, maybeInc, Use::NoCoercion, &_1, &_2)) + return false; + } + + return f.closeLoop(loopEntry, afterLoop); +} + +static bool +CheckDoWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLabels) +{ + JS_ASSERT(whileStmt->isKind(PNK_DOWHILE)); + ParseNode *body = BinaryLeft(whileStmt); + ParseNode *cond = BinaryRight(whileStmt); + + MBasicBlock *loopEntry; + if (!f.startPendingLoop(whileStmt, &loopEntry)) + return false; + + if (!CheckStatement(f, body)) + return false; + + if (!f.bindContinues(whileStmt, maybeLabels)) + return false; + + MDefinition *condDef; + Type condType; + if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) + return false; + + if (!condType.isInt()) + return f.fail("Condition of while loop must be boolish", cond); + + return f.branchAndCloseDoWhileLoop(condDef, loopEntry); +} + +static bool +CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels) +{ + JS_ASSERT(labeledStmt->isKind(PNK_COLON)); + PropertyName *label = LabeledStatementLabel(labeledStmt); + ParseNode *stmt = LabeledStatementStatement(labeledStmt); + + if (maybeLabels) { + if (!maybeLabels->append(label)) + return false; + if (!CheckStatement(f, stmt, maybeLabels)) + return false; + return true; + } + + LabelVector labels(f.cx()); + if (!labels.append(label)) + return false; + + if (!CheckStatement(f, stmt, &labels)) + return false; + + return f.bindLabeledBreaks(&labels); +} + +static bool +CheckIf(FunctionCompiler &f, ParseNode *ifStmt) +{ + JS_ASSERT(ifStmt->isKind(PNK_IF)); + ParseNode *cond = TernaryKid1(ifStmt); + ParseNode *thenStmt = TernaryKid2(ifStmt); + ParseNode *elseStmt = TernaryKid3(ifStmt); + + MDefinition *condDef; + Type condType; + if (!CheckExpr(f, cond, Use::NoCoercion, &condDef, &condType)) + return false; + + if (!condType.isInt()) + return f.fail("Condition of if must be boolish", cond); + + MBasicBlock *thenBlock, *elseBlock; + if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock)) + return false; + + if (!CheckStatement(f, thenStmt)) + return false; + + if (!elseStmt) { + f.joinIf(elseBlock); + } else { + MBasicBlock *thenEnd = f.switchToElse(elseBlock); + if (!CheckStatement(f, elseStmt)) + return false; + if (!f.joinIfElse(thenEnd)) + return false; + } + + return true; +} + +static bool +CheckCaseExpr(FunctionCompiler &f, ParseNode *caseExpr, int32_t *value) +{ + if (!IsNumericLiteral(caseExpr)) + return f.fail("Switch case expression must be an integer literal", caseExpr); + + NumLit literal = ExtractNumericLiteral(caseExpr); + switch (literal.which()) { + case NumLit::Fixnum: + case NumLit::NegativeInt: + *value = literal.toInt32(); + break; + case NumLit::OutOfRangeInt: + case NumLit::BigUnsigned: + return f.fail("Switch case expression out of integer range", caseExpr); + case NumLit::Double: + return f.fail("Switch case expression must be an integer literal", caseExpr); + } + + return true; +} + +static bool +CheckDefaultAtEnd(FunctionCompiler &f, ParseNode *stmt) +{ + for (; stmt; stmt = NextNode(stmt)) { + JS_ASSERT(stmt->isKind(PNK_CASE) || stmt->isKind(PNK_DEFAULT)); + if (stmt->isKind(PNK_DEFAULT) && NextNode(stmt) != NULL) + return f.fail("default label must be at the end", stmt); + } + + return true; +} + +static bool +CheckSwitchRange(FunctionCompiler &f, ParseNode *stmt, int32_t *low, int32_t *high, + int32_t *tableLength) +{ + if (stmt->isKind(PNK_DEFAULT)) { + *low = 0; + *high = -1; + *tableLength = 0; + return true; + } + + int32_t i = 0; + if (!CheckCaseExpr(f, CaseExpr(stmt), &i)) + return false; + + *low = *high = i; + + for (stmt = NextNode(stmt); stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) { + int32_t i = 0; + if (!CheckCaseExpr(f, CaseExpr(stmt), &i)) + return false; + + *low = Min(*low, i); + *high = Max(*high, i); + } + + int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1; + if (i64 > 512*1024*1024) + return f.fail("All switch statements generate tables; this table would be bigger than 512MiB", stmt); + + *tableLength = int32_t(i64); + return true; +} + +static bool +CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt) +{ + JS_ASSERT(switchStmt->isKind(PNK_SWITCH)); + ParseNode *switchExpr = BinaryLeft(switchStmt); + ParseNode *switchBody = BinaryRight(switchStmt); + + if (!switchBody->isKind(PNK_STATEMENTLIST)) + return f.fail("Switch body may not contain 'let' declarations", switchBody); + + MDefinition *exprDef; + Type exprType; + if (!CheckExpr(f, switchExpr, Use::NoCoercion, &exprDef, &exprType)) + return false; + + if (!exprType.isSigned()) + return f.fail("Switch expression must be a signed integer", switchExpr); + + ParseNode *stmt = ListHead(switchBody); + + if (!CheckDefaultAtEnd(f, stmt)) + return false; + + if (!stmt) + return true; + + int32_t low = 0, high = 0, tableLength = 0; + if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength)) + return false; + + CaseVector cases(f.cx()); + if (!cases.resize(tableLength)) + return false; + + MBasicBlock *switchBlock; + if (!f.startSwitch(switchStmt, exprDef, low, high, &switchBlock)) + return false; + + for (; stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) { + int32_t caseValue = ExtractNumericLiteral(CaseExpr(stmt)).toInt32(); + unsigned caseIndex = caseValue - low; + + if (cases[caseIndex]) + return f.fail("No duplicate case labels", stmt); + + if (!f.startSwitchCase(switchBlock, &cases[caseIndex])) + return false; + + if (!CheckStatement(f, CaseBody(stmt))) + return false; + } + + MBasicBlock *defaultBlock; + if (!f.startSwitchDefault(switchBlock, &cases, &defaultBlock)) + return false; + + if (stmt && stmt->isKind(PNK_DEFAULT)) { + if (!CheckStatement(f, CaseBody(stmt))) + return false; + } + + return f.joinSwitch(switchBlock, cases, defaultBlock); +} + +static bool +CheckReturn(FunctionCompiler &f, ParseNode *returnStmt) +{ + JS_ASSERT(returnStmt->isKind(PNK_RETURN)); + ParseNode *expr = UnaryKid(returnStmt); + + if (!expr) { + if (f.func().returnType().which() != RetType::Void) + return f.fail("All return statements must return void", returnStmt); + + f.returnVoid(); + return true; + } + + MDefinition *def; + Type type; + if (!CheckExpr(f, expr, Use::NoCoercion, &def, &type)) + return false; + + if (!(type <= f.func().returnType())) + return f.fail("All returns must return the same type", expr); + + if (f.func().returnType().which() == RetType::Void) + f.returnVoid(); + else + f.returnExpr(def); + return true; +} + +static bool +CheckStatements(FunctionCompiler &f, ParseNode *stmtHead) +{ + for (ParseNode *stmt = stmtHead; stmt; stmt = NextNode(stmt)) { + if (!CheckStatement(f, stmt)) + return false; + } + + return true; +} + +static bool +CheckStatementList(FunctionCompiler &f, ParseNode *stmt) +{ + JS_ASSERT(stmt->isKind(PNK_STATEMENTLIST)); + return CheckStatements(f, ListHead(stmt)); +} + +static bool +CheckStatement(FunctionCompiler &f, ParseNode *stmt, LabelVector *maybeLabels) +{ + JS_CHECK_RECURSION(f.cx(), return false); + + if (!f.m().alloc().ensureBallast()) + return false; + + switch (stmt->getKind()) { + case PNK_SEMI: return CheckExprStatement(f, stmt); + case PNK_WHILE: return CheckWhile(f, stmt, maybeLabels); + case PNK_FOR: return CheckFor(f, stmt, maybeLabels); + case PNK_DOWHILE: return CheckDoWhile(f, stmt, maybeLabels); + case PNK_COLON: return CheckLabel(f, stmt, maybeLabels); + case PNK_IF: return CheckIf(f, stmt); + case PNK_SWITCH: return CheckSwitch(f, stmt); + case PNK_RETURN: return CheckReturn(f, stmt); + case PNK_STATEMENTLIST: return CheckStatementList(f, stmt); + case PNK_BREAK: return f.addBreak(LoopControlMaybeLabel(stmt)); + case PNK_CONTINUE: return f.addContinue(LoopControlMaybeLabel(stmt)); + default:; + } + + return f.fail("Unexpected statement kind", stmt); +} + +static bool +CheckVariableDecl(ModuleCompiler &m, ParseNode *var, FunctionCompiler::LocalMap *locals) +{ + if (!IsDefinition(var)) + return m.fail("Local variable names must not restate argument names", var); + + PropertyName *name = var->name(); + + if (!CheckIdentifier(m, name, var)) + return false; + + ParseNode *initNode = MaybeDefinitionInitializer(var); + if (!initNode) + return m.fail("Variable needs explicit type declaration via an initial value", var); + + if (!IsNumericLiteral(initNode)) + return m.fail("Variable initialization value needs to be a numeric literal", initNode); + + NumLit literal = ExtractNumericLiteral(initNode); + VarType type; + switch (literal.which()) { + case NumLit::Fixnum: + case NumLit::NegativeInt: + case NumLit::BigUnsigned: + type = VarType::Int; + break; + case NumLit::Double: + type = VarType::Double; + break; + case NumLit::OutOfRangeInt: + return m.fail("Variable initializer is out of representable integer range", initNode); + } + + FunctionCompiler::LocalMap::AddPtr p = locals->lookupForAdd(name); + if (p) + return m.fail("Local names must be unique", initNode); + + unsigned slot = locals->count(); + if (!locals->add(p, name, FunctionCompiler::Local(type, slot, literal.value()))) + return false; + + return true; +} + +static bool +CheckVariableDecls(ModuleCompiler &m, FunctionCompiler::LocalMap *locals, ParseNode **stmtIter) +{ + ParseNode *stmt = *stmtIter; + + for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNode(stmt)) { + for (ParseNode *var = VarListHead(stmt); var; var = NextNode(var)) { + if (!CheckVariableDecl(m, var, locals)) + return false; + } + } + + *stmtIter = stmt; + return true; +} + +static bool +CheckFunctionBody(ModuleCompiler &m, ModuleCompiler::Func &func) +{ + // CheckFunctionSignature already has already checked the + // function head as well as argument type declarations. The ParseNode* + // stored in f.body points to the first non-argument statement. + ParseNode *stmtIter = func.body(); + + FunctionCompiler::LocalMap locals(m.cx()); + if (!locals.init()) + return false; + + unsigned numFormals; + ParseNode *arg = FunctionArgsList(func.fn(), &numFormals); + for (unsigned i = 0; i < numFormals; i++, arg = NextNode(arg)) { + if (!locals.putNew(arg->name(), FunctionCompiler::Local(func.argType(i), i))) + return false; + } + + if (!CheckVariableDecls(m, &locals, &stmtIter)) + return false; + + FunctionCompiler f(m, func, Move(locals)); + if (!f.init()) + return false; + + if (!CheckStatements(f, stmtIter)) + return false; + + f.returnVoid(); + + m.masm().bind(func.codeLabel()); + + ScopedJSDeletePtr codegen(CompileBackEnd(&f.mirGen(), &m.masm())); + if (!codegen) + return m.fail("Internal compiler failure (probably out of memory)", func.fn()); + + if (!m.collectAccesses(f.mirGen())) + return false; + + // Unlike regular IonMonkey which links and generates a new IonCode for + // every function, we accumulate all the functions in the module in a + // single MacroAssembler and link at end. Linking asm.js doesn't require a + // CodeGenerator so we can destory it now. + return true; +} + +static const unsigned CodeAlignment = 8; + +static bool +CheckFunctionBodies(ModuleCompiler &m) +{ + for (unsigned i = 0; i < m.numFunctions(); i++) { + if (!CheckFunctionBody(m, m.function(i))) + return false; + + // A single MacroAssembler is reused for all function compilations so + // that there is a single linear code segment for each module. To avoid + // spiking memory, each FunctionCompiler creates a LifoAllocScope so + // that all MIR/LIR nodes are freed after each function is compiled. + // This method is responsible for cleaning out any dangling pointers + // that the MacroAssembler may have kept. + m.masm().resetForNewCodeGenerator(); + + // Align internal function headers. + m.masm().align(CodeAlignment); + } + + return true; +} + +static RegisterSet AllRegs = RegisterSet(GeneralRegisterSet(Registers::AllMask), + FloatRegisterSet(FloatRegisters::AllMask)); +static RegisterSet NonVolatileRegs = RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask), + FloatRegisterSet(FloatRegisters::NonVolatileMask)); + +static void +LoadAsmJSActivationIntoRegister(MacroAssembler &masm, Register reg) +{ + masm.movePtr(ImmWord(GetIonContext()->compartment->rt), reg); + size_t offset = offsetof(JSRuntime, mainThread) + + PerThreadData::offsetOfAsmJSActivationStackReadOnly(); + masm.loadPtr(Address(reg, offset), reg); +} + +static void +LoadJSContextFromActivation(MacroAssembler &masm, Register activation, Register dest) +{ + masm.loadPtr(Address(activation, AsmJSActivation::offsetOfContext()), dest); +} + +static void +AssertStackAlignment(MacroAssembler &masm) +{ + JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0); +#ifdef DEBUG + Label ok; + JS_ASSERT(IsPowerOfTwo(StackAlignment)); + masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok); + masm.breakpoint(); + masm.bind(&ok); +#endif +} + +static unsigned +StackArgBytes(const MIRTypeVector &argTypes) +{ + ABIArgIter iter(argTypes); + while (!iter.done()) + iter++; + return iter.stackBytesConsumedSoFar(); +} + +static unsigned +StackDecrementForCall(MacroAssembler &masm, const MIRTypeVector &argTypes, unsigned extraBytes = 0) +{ + // Include extra padding so that, after pushing the arguments and + // extraBytes, the stack is aligned for a call instruction. + unsigned argBytes = StackArgBytes(argTypes); + unsigned alreadyPushed = AlignmentAtPrologue + masm.framePushed(); + return AlignBytes(alreadyPushed + extraBytes + argBytes, StackAlignment) - alreadyPushed; +} + +static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * STACK_SLOT_SIZE + + NonVolatileRegs.fpus().size() * sizeof(double); + +static bool +GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc) +{ + MacroAssembler &masm = m.masm(); + + // In constrast to the system ABI, the Ion convention is that all registers + // are clobbered by calls. Thus, we must save the caller's non-volatile + // registers. + // + // NB: GenerateExits assumes that masm.framePushed() == 0 before + // PushRegsInMask(NonVolatileRegs). + masm.setFramePushed(0); + masm.PushRegsInMask(NonVolatileRegs); + + // Remember the stack pointer in the current AsmJSActivation. This will be + // used by error exit paths to set the stack pointer back to what it was + // right after the (C++) caller's non-volatile registers were saved so that + // they can be restored. + JS_ASSERT(masm.framePushed() == FramePushedAfterSave); + Register activation = ABIArgGenerator::NonArgReturnVolatileReg1; + LoadAsmJSActivationIntoRegister(masm, activation); + masm.movePtr(StackPointer, Operand(activation, AsmJSActivation::offsetOfErrorRejoinSP())); + +#if defined(JS_CPU_X64) + // Install the heap pointer into the globally-pinned HeapReg. The heap + // pointer is stored in the global data section and is patched at dynamic + // link time. + CodeOffsetLabel label = masm.loadRipRelativeInt64(HeapReg); + m.addGlobalAccess(AsmJSGlobalAccess(label.offset(), m.module().heapOffset())); +#endif + + Register argv = ABIArgGenerator::NonArgReturnVolatileReg1; + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg2; +#if defined(JS_CPU_X86) + masm.movl(Operand(StackPointer, NativeFrameSize + masm.framePushed()), argv); +#elif defined(JS_CPU_X64) + masm.movq(IntArgReg0, argv); + masm.Push(argv); +#endif + + // Bump the stack for the call. + const ModuleCompiler::Func &func = *m.lookupFunction(exportedFunc.name()); + unsigned stackDec = StackDecrementForCall(masm, func.argMIRTypes()); + masm.reserveStack(stackDec); + + for (ABIArgIter iter(func.argMIRTypes()); !iter.done(); iter++) { + Operand src(argv, iter.index() * sizeof(uint64_t)); + switch (iter->kind()) { + case ABIArg::GPR: + masm.load32(src, iter->gpr()); + break; + case ABIArg::FPU: + masm.loadDouble(src, iter->fpu()); + break; + case ABIArg::Stack: + if (iter.mirType() == MIRType_Int32) { + masm.load32(src, scratch); + masm.storePtr(scratch, Operand(StackPointer, iter->offsetFromArgBase())); + } else { + JS_ASSERT(iter.mirType() == MIRType_Double); + masm.loadDouble(src, ScratchFloatReg); + masm.storeDouble(ScratchFloatReg, Operand(StackPointer, iter->offsetFromArgBase())); + } + break; + } + } + + AssertStackAlignment(masm); + masm.call(func.codeLabel()); + + masm.freeStack(stackDec); + +#if defined(JS_CPU_X86) + masm.movl(Operand(StackPointer, NativeFrameSize + masm.framePushed()), argv); +#elif defined(JS_CPU_X64) + masm.Pop(argv); +#endif + + // Store return value in argv[0] + switch (func.returnType().which()) { + case RetType::Void: + break; + case RetType::Signed: + masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0)); + break; + case RetType::Double: + masm.canonicalizeDouble(ReturnFloatReg); + masm.storeDouble(ReturnFloatReg, Address(argv, 0)); + break; + } + + // Restore clobbered registers. + masm.PopRegsInMask(NonVolatileRegs); + JS_ASSERT(masm.framePushed() == 0); + + masm.move32(Imm32(true), ReturnReg); + masm.ret(); + return true; +} + +static bool +GenerateEntries(ModuleCompiler &m) +{ + for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) { + m.setEntryOffset(i); + if (!GenerateEntry(m, m.module().exportedFunction(i))) + return false; + } + + return true; +} + +static int32_t +InvokeFromAsmJS_Ignore(JSContext *cx, AsmJSModule::ExitDatum *exitDatum, int32_t argc, Value *argv) +{ + RootedValue fval(cx, ObjectValue(*exitDatum->fun)); + RootedValue rval(cx); + if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval.address())) + return false; + + return true; +} + +static int32_t +InvokeFromAsmJS_ToInt32(JSContext *cx, AsmJSModule::ExitDatum *exitDatum, int32_t argc, Value *argv) +{ + RootedValue fval(cx, ObjectValue(*exitDatum->fun)); + RootedValue rval(cx); + if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval.address())) + return false; + + int32_t i32; + if (!ToInt32(cx, rval, &i32)) + return false; + argv[0] = Int32Value(i32); + + return true; +} + +static int32_t +InvokeFromAsmJS_ToNumber(JSContext *cx, AsmJSModule::ExitDatum *exitDatum, int32_t argc, Value *argv) +{ + RootedValue fval(cx, ObjectValue(*exitDatum->fun)); + RootedValue rval(cx); + if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval.address())) + return false; + + double dbl; + if (!ToNumber(cx, rval, &dbl)) + return false; + argv[0] = DoubleValue(dbl); + + return true; +} + +// See "asm.js FFI calls" comment above. +static void +GenerateFFIExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, unsigned exitIndex, + Label *throwLabel) +{ + MacroAssembler &masm = m.masm(); + masm.align(CodeAlignment); + m.setExitOffset(exitIndex); + + MIRType typeArray[] = { MIRType_Pointer, // cx + MIRType_Pointer, // exitDatum + MIRType_Int32, // argc + MIRType_Pointer }; // argv + MIRTypeVector invokeArgTypes(m.cx()); + invokeArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray)); + + // Reserve space for a call to InvokeFromAsmJS_* and an array of values + // passed to this FFI call. + unsigned arraySize = Max(1, exit.argTypes().length()) * sizeof(Value); + unsigned stackDec = StackDecrementForCall(masm, invokeArgTypes, arraySize); + masm.setFramePushed(0); + masm.reserveStack(stackDec); + + // Fill the argument array. + unsigned offsetToCallerStackArgs = NativeFrameSize + masm.framePushed(); + unsigned offsetToArgv = StackArgBytes(invokeArgTypes); + for (ABIArgIter i(exit.argTypes()); !i.done(); i++) { + Address dstAddr = Address(StackPointer, offsetToArgv + i.index() * sizeof(Value)); + switch (i->kind()) { + case ABIArg::GPR: + masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr); + break; + case ABIArg::FPU: + masm.canonicalizeDouble(i->fpu()); + masm.storeDouble(i->fpu(), dstAddr); + break; + case ABIArg::Stack: + if (i.mirType() == MIRType_Int32) { + Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase()); + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1; + masm.load32(src, scratch); + masm.storeValue(JSVAL_TYPE_INT32, scratch, dstAddr); + } else { + JS_ASSERT(i.mirType() == MIRType_Double); + Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase()); + masm.loadDouble(src, ScratchFloatReg); + masm.canonicalizeDouble(ScratchFloatReg); + masm.storeDouble(ScratchFloatReg, dstAddr); + } + break; + } + } + + // Prepare the arguments for the call to InvokeFromAsmJS_*. + ABIArgIter i(invokeArgTypes); + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1; + Register activation = ABIArgGenerator::NonArgReturnVolatileReg2; + LoadAsmJSActivationIntoRegister(masm, activation); + + // argument 0: cx + if (i->kind() == ABIArg::GPR) { + LoadJSContextFromActivation(masm, activation, i->gpr()); + } else { + LoadJSContextFromActivation(masm, activation, scratch); + masm.movePtr(scratch, Operand(StackPointer, i->offsetFromArgBase())); + } + i++; + + // argument 1: exitDatum + CodeOffsetLabel label; +#if defined(JS_CPU_X64) + label = masm.leaRipRelative(i->gpr()); +#else + if (i->kind() == ABIArg::GPR) { + label = masm.movlWithPatch(Imm32(0), i->gpr()); + } else { + label = masm.movlWithPatch(Imm32(0), scratch); + masm.movl(scratch, Operand(StackPointer, i->offsetFromArgBase())); + } +#endif + unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex); + m.addGlobalAccess(AsmJSGlobalAccess(label.offset(), globalDataOffset)); + i++; + + // argument 2: argc + unsigned argc = exit.argTypes().length(); + if (i->kind() == ABIArg::GPR) + masm.mov(Imm32(argc), i->gpr()); + else + masm.move32(Imm32(argc), Operand(StackPointer, i->offsetFromArgBase())); + i++; + + // argument 3: argv + Address argv(StackPointer, offsetToArgv); + if (i->kind() == ABIArg::GPR) { + masm.computeEffectiveAddress(argv, i->gpr()); + } else { + masm.computeEffectiveAddress(argv, scratch); + masm.movePtr(scratch, Operand(StackPointer, i->offsetFromArgBase())); + } + i++; + JS_ASSERT(i.done()); + + // Make the call, test whether it succeeded, and extract the return value. + AssertStackAlignment(masm); + switch (exit.use().which()) { + case Use::NoCoercion: + masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_Ignore))); + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); + break; + case Use::ToInt32: + masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_ToInt32))); + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); + masm.unboxInt32(argv, ReturnReg); + break; + case Use::ToNumber: + masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_ToNumber))); + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); + masm.loadDouble(argv, ReturnFloatReg); + break; + case Use::AddOrSub: + JS_NOT_REACHED("Should have been a type error"); + } + + // Note: the caller is IonMonkey code which means there are no non-volatile + // registers to restore. + masm.freeStack(stackDec); + masm.ret(); +} + +// The stack-overflow exit is called when the stack limit has definitely been +// exceeded. In this case, we can clobber everything since we are about to pop +// all the frames. +static void +GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel) +{ + MacroAssembler &masm = m.masm(); + masm.align(CodeAlignment); + masm.bind(&m.stackOverflowLabel()); + +#if defined(JS_CPU_X86) + // Ensure that at least one slot is pushed for passing 'cx' below. + masm.push(Imm32(0)); +#endif + + // We know that StackPointer is word-aligned, but nothing past that. Thus, + // we must align StackPointer dynamically. Don't worry about restoring + // StackPointer since throwLabel will clobber StackPointer immediately. + masm.andPtr(Imm32(~(StackAlignment - 1)), StackPointer); + if (ShadowStackSpace) + masm.subPtr(Imm32(ShadowStackSpace), StackPointer); + + // Prepare the arguments for the call to js_ReportOverRecursed. +#if defined(JS_CPU_X86) + LoadAsmJSActivationIntoRegister(masm, eax); + LoadJSContextFromActivation(masm, eax, eax); + masm.storePtr(eax, Address(StackPointer, 0)); +#elif defined(JS_CPU_X64) + LoadAsmJSActivationIntoRegister(masm, IntArgReg0); + LoadJSContextFromActivation(masm, IntArgReg0, IntArgReg0); +#else +# error "ARM here" +#endif + + void (*pf)(JSContext*) = js_ReportOverRecursed; + masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, pf))); + masm.jmp(throwLabel); +} + +// The operation-callback exit is called from arbitrarily-interrupted asm.js +// code. That means we must first save *all* registers and restore *all* +// registers when we resume. The address to resume to (assuming that +// js_HandleExecutionInterrupt doesn't indicate that the execution should be +// aborted) is stored in AsmJSActivation::resumePC_. Unfortunately, loading +// this requires a scratch register which we don't have after restoring all +// registers. To hack around this, push the resumePC on the stack so that it +// can be popped directly into PC. +static void +GenerateOperationCallbackExit(ModuleCompiler &m, Label *throwLabel) +{ + MacroAssembler &masm = m.masm(); + masm.align(CodeAlignment); + masm.bind(&m.operationCallbackLabel()); + + // Be very careful here not to perturb the machine state before saving it + // to the stack. In particular, add/sub instructions may set conditions in + // the flags register. + masm.push(Imm32(0)); // space for resumePC + masm.pushFlags(); // after this we are safe to use sub + masm.setFramePushed(0); // set to zero so we can use masm.framePushed() below + masm.PushRegsInMask(AllRegs); // save all GP/FP registers + + Register activation = ABIArgGenerator::NonArgReturnVolatileReg1; + Register scratch = ABIArgGenerator::NonArgReturnVolatileReg2; + + // Store resumePC into the reserved space. + LoadAsmJSActivationIntoRegister(masm, activation); + masm.loadPtr(Address(activation, AsmJSActivation::offsetOfResumePC()), scratch); + masm.storePtr(scratch, Address(StackPointer, masm.framePushed() + sizeof(void*))); + + // We know that StackPointer is word-aligned, but not necessarily + // stack-aligned, so we need to align it dynamically. + masm.mov(StackPointer, ABIArgGenerator::NonVolatileReg); +#if defined(JS_CPU_X86) + // Ensure that at least one slot is pushed for passing 'cx' below. + masm.push(Imm32(0)); +#endif + masm.andPtr(Imm32(~(StackAlignment - 1)), StackPointer); + if (ShadowStackSpace) + masm.subPtr(Imm32(ShadowStackSpace), StackPointer); + + // argument 0: cx +#if defined(JS_CPU_X86) + LoadJSContextFromActivation(masm, activation, scratch); + masm.storePtr(scratch, Address(StackPointer, 0)); +#elif defined(JS_CPU_X64) + LoadJSContextFromActivation(masm, activation, IntArgReg0); +#endif + + JSBool (*pf)(JSContext*) = js_HandleExecutionInterrupt; + masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, pf))); + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); + + // Restore the StackPointer to it's position before the call. + masm.mov(ABIArgGenerator::NonVolatileReg, StackPointer); + + // Restore the machine state to before the interrupt. + masm.PopRegsInMask(AllRegs); // restore all GP/FP registers + masm.popFlags(); // after this, nothing that sets conditions + masm.ret(); // pop resumePC into PC +} + +// If an exception is thrown, simply pop all frames (since asm.js does not +// contain try/catch). To do this: +// 1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry. +// 2. PopRegsInMask to restore the caller's non-volatile registers. +// 3. Return (to CallAsmJS). +static void +GenerateThrowExit(ModuleCompiler &m, Label *throwLabel) +{ + MacroAssembler &masm = m.masm(); + masm.align(CodeAlignment); + masm.bind(throwLabel); + + Register activation = ABIArgGenerator::NonArgReturnVolatileReg1; + LoadAsmJSActivationIntoRegister(masm, activation); + + masm.setFramePushed(FramePushedAfterSave); + masm.mov(Operand(activation, AsmJSActivation::offsetOfErrorRejoinSP()), StackPointer); + masm.PopRegsInMask(NonVolatileRegs); + JS_ASSERT(masm.framePushed() == 0); + + masm.mov(Imm32(0), ReturnReg); + masm.ret(); +} + +static bool +GenerateExits(ModuleCompiler &m) +{ + Label throwLabel; + + for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) { + GenerateFFIExit(m, r.front().key, r.front().value, &throwLabel); + if (m.masm().oom()) + return false; + } + + if (m.stackOverflowLabel().used()) + GenerateStackOverflowExit(m, &throwLabel); + + GenerateOperationCallbackExit(m, &throwLabel); + + GenerateThrowExit(m, &throwLabel); + return true; +} + +static bool +CheckModule(JSContext *cx, TokenStream &ts, ParseNode *fn, ScopedJSDeletePtr *module) +{ + ModuleCompiler m(cx, ts); + if (!m.init()) + return false; + + if (PropertyName *moduleFunctionName = FunctionName(fn)) { + if (!CheckModuleLevelName(m, moduleFunctionName, fn)) + return false; + m.initModuleFunctionName(moduleFunctionName); + } + + ParseNode *stmtIter = NULL; + + if (!CheckFunctionHead(m, fn, &stmtIter)) + return false; + + if (!CheckModuleArguments(m, fn)) + return false; + + if (!SkipUseAsmDirective(m, &stmtIter)) + return false; + + if (!CheckModuleGlobals(m, &stmtIter)) + return false; + + if (!CheckFunctionSignatures(m, &stmtIter)) + return false; + + if (!CheckFuncPtrTables(m, &stmtIter)) + return false; + + if (!CheckModuleExports(m, fn, &stmtIter)) + return false; + + if (stmtIter) + return m.fail("The top-level export (return) must be the last statement.", stmtIter); + + m.setFirstPassComplete(); + + if (!CheckFunctionBodies(m)) + return false; + + m.setSecondPassComplete(); + + if (!GenerateEntries(m)) + return false; + + if (!GenerateExits(m)) + return false; + + return m.finish(module); +} + +#endif // defined(JS_ASMJS) + +static bool +Warn(JSContext *cx, int code, const char *str = NULL) +{ + return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage, + NULL, code, str); +} + +extern bool +EnsureAsmJSSignalHandlersInstalled(); + +bool +js::CompileAsmJS(JSContext *cx, TokenStream &ts, ParseNode *fn, HandleScript script) +{ + if (!JSC::MacroAssembler().supportsFloatingPoint()) + return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support"); + + if (!cx->hasOption(JSOPTION_ASMJS)) + return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.experimental_asmjs " + "in about:config"); + + if (cx->compartment->debugMode()) + return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger"); + +#ifdef JS_ASMJS + if (!EnsureAsmJSSignalHandlersInstalled()) + return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support"); + + ScopedJSDeletePtr module; + if (!CheckModule(cx, ts, fn, &module)) + return !cx->isExceptionPending(); + + RootedObject moduleObj(cx, NewAsmJSModuleObject(cx, &module)); + if (!moduleObj) + return false; + + JS_ASSERT(!script->asmJS); + script->asmJS.init(moduleObj); + + return Warn(cx, JSMSG_USE_ASM_TYPE_OK); +#else + return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Platform not supported"); +#endif +} + +JSBool +js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + +#ifdef JS_ASMJS + bool available = JSC::MacroAssembler().supportsFloatingPoint() && + !cx->compartment->debugMode(); +#else + bool available = false; +#endif + + args.rval().set(BooleanValue(available)); + return true; +} + diff --git a/js/src/ion/AsmJS.h b/js/src/ion/AsmJS.h new file mode 100644 index 00000000000..52f09fc1dd4 --- /dev/null +++ b/js/src/ion/AsmJS.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et 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/. */ + +#if !defined(jsion_asmjs_h__) +#define jsion_asmjs_h__ + +// asm.js compilation is only available on desktop x86/x64 at the moment. +// Don't panic, mobile support is coming soon. +#if defined(JS_ION) && \ + !defined(ANDROID) && \ + (defined(JS_CPU_X86) || defined(JS_CPU_X64)) && \ + (defined(__linux__) || defined(XP_WIN) || defined(XP_MACOSX)) +# define JS_ASMJS +#endif + +namespace js { + +class SPSProfiler; +class AsmJSModule; +namespace frontend { struct TokenStream; struct ParseNode; } + +// Return whether asm.js optimization is inhibitted by the platform or +// dynamically disabled. (Exposed as JSNative for shell testing.) +extern JSBool +IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp); + +// Called after parsing a function 'fn' which contains the "use asm" directive. +// This function performs type-checking and code-generation. If type-checking +// succeeds, the generated module is assigned to script->asmJS. Otherwise, a +// warning will be emitted and script->asmJS is left null. The function returns +// 'false' only if a real JS semantic error (probably OOM) is pending. +extern bool +CompileAsmJS(JSContext *cx, frontend::TokenStream &ts, frontend::ParseNode *fn, HandleScript s); + +// Called by the JSOP_LINKASMJS opcode (which is emitted as the first opcode of +// a "use asm" function which successfully typechecks). This function performs +// the validation and dynamic linking of a module to it's given arguments. If +// validation succeeds, the module's return value (it's exports) are returned +// as an object in 'rval' and the interpreter should return 'rval' immediately. +// Otherwise, there was a validation error and execution should continue +// normally in the interpreter. The function returns 'false' only if a real JS +// semantic error (OOM or exception thrown when executing GetProperty on the +// arguments) is pending. +extern bool +LinkAsmJS(JSContext *cx, StackFrame *fp, MutableHandleValue rval); + +// Force any currently-executing asm.js code to call +// js_HandleExecutionInterrupt. +void +TriggerOperationCallbackForAsmJSCode(JSRuntime *rt); + +// The JSRuntime maintains a stack of AsmJSModule activations. An "activation" +// of module A is an initial call from outside A into a function inside A, +// followed by a sequence of calls inside A, and terminated by a call that +// leaves A. The AsmJSActivation stack serves three purposes: +// - record the correct cx to pass to VM calls from asm.js; +// - record enough information to pop all the frames of an activation if an +// exception is thrown; +// - record the information necessary for asm.js signal handlers to safely +// recover from (expected) out-of-bounds access, the operation callback, +// stack overflow, division by zero, etc. +class AsmJSActivation +{ + JSContext *cx_; + const AsmJSModule &module_; + unsigned entryIndex_; + AsmJSActivation *prev_; + void *errorRejoinSP_; + SPSProfiler *profiler_; + void *resumePC_; + + public: + AsmJSActivation(JSContext *cx, const AsmJSModule &module, unsigned entryIndex); + ~AsmJSActivation(); + + const AsmJSModule &module() const { return module_; } + + // Read by JIT code: + static unsigned offsetOfContext() { return offsetof(AsmJSActivation, cx_); } + static unsigned offsetOfResumePC() { return offsetof(AsmJSActivation, resumePC_); } + + // Initialized by JIT code: + static unsigned offsetOfErrorRejoinSP() { return offsetof(AsmJSActivation, errorRejoinSP_); } + + // Set from SIGSEGV handler: + void setResumePC(void *pc) { resumePC_ = pc; } +}; + +// The asm.js spec requires that the ArrayBuffer's byteLength be a multiple of 4096. +static const size_t AsmJSAllocationGranularity = 4096; + +// On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the +// byteLength portion of which is accessible) so that out-of-bounds accesses +// (made using a uint32 index) are guaranteed to raise a SIGSEGV. +# ifdef JS_CPU_X64 +static const size_t AsmJSBufferProtectedSize = 4 * 1024ULL * 1024ULL * 1024ULL; +# endif + +} // namespace js + +#endif // jsion_asmjs_h__ diff --git a/js/src/ion/AsmJSLink.cpp b/js/src/ion/AsmJSLink.cpp new file mode 100644 index 00000000000..430bcda5f3d --- /dev/null +++ b/js/src/ion/AsmJSLink.cpp @@ -0,0 +1,403 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsmath.h" +#include "jscntxt.h" + +#include "jstypedarrayinlines.h" + +#include "AsmJS.h" +#include "AsmJSModule.h" + +using namespace js; +using namespace js::ion; +using namespace mozilla; + +static bool +LinkFail(JSContext *cx, const char *str) +{ + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage, + NULL, JSMSG_USE_ASM_LINK_FAIL, str); + return false; +} + +static bool +ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global global, + HandleValue importVal) +{ + JS_ASSERT(global.which() == AsmJSModule::Global::Variable); + + void *datum = module.globalVarIndexToGlobalDatum(global.varIndex()); + + switch (global.varInitKind()) { + case AsmJSModule::Global::InitConstant: { + const Value &v = global.varInitConstant(); + if (v.isInt32()) + *(int32_t *)datum = v.toInt32(); + else + *(double *)datum = v.toDouble(); + break; + } + case AsmJSModule::Global::InitImport: { + RootedPropertyName field(cx, global.varImportField()); + RootedValue v(cx); + if (!GetProperty(cx, importVal, field, &v)) + return false; + + switch (global.varImportCoercion()) { + case AsmJS_ToInt32: + if (!ToInt32(cx, v, (int32_t *)datum)) + return false; + break; + case AsmJS_ToNumber: + if (!ToNumber(cx, v, (double *)datum)) + return false; + break; + } + break; + } + } + + return true; +} + +static bool +ValidateFFI(JSContext *cx, AsmJSModule::Global global, HandleValue importVal, + AutoObjectVector *ffis) +{ + RootedPropertyName field(cx, global.ffiField()); + RootedValue v(cx); + if (!GetProperty(cx, importVal, field, &v)) + return false; + + if (!v.isObject() || !v.toObject().isFunction()) + return LinkFail(cx, "FFI imports must be functions"); + + (*ffis)[global.ffiIndex()] = v.toObject().toFunction(); + return true; +} + +static bool +ValidateArrayView(JSContext *cx, AsmJSModule::Global global, HandleValue globalVal, + HandleValue bufferVal) +{ + RootedPropertyName field(cx, global.viewName()); + RootedValue v(cx); + if (!GetProperty(cx, globalVal, field, &v)) + return false; + + if (!IsTypedArrayConstructor(v, global.viewType())) + return LinkFail(cx, "bad typed array constructor"); + + return true; +} + +static bool +ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global global, HandleValue globalVal) +{ + RootedValue v(cx); + if (!GetProperty(cx, globalVal, cx->names().Math, &v)) + return false; + RootedPropertyName field(cx, global.mathName()); + if (!GetProperty(cx, v, field, &v)) + return false; + + Native native = NULL; + switch (global.mathBuiltin()) { + case AsmJSMathBuiltin_sin: native = math_sin; break; + case AsmJSMathBuiltin_cos: native = math_cos; break; + case AsmJSMathBuiltin_tan: native = math_tan; break; + case AsmJSMathBuiltin_asin: native = math_asin; break; + case AsmJSMathBuiltin_acos: native = math_acos; break; + case AsmJSMathBuiltin_atan: native = math_atan; break; + case AsmJSMathBuiltin_ceil: native = js_math_ceil; break; + case AsmJSMathBuiltin_floor: native = js_math_floor; break; + case AsmJSMathBuiltin_exp: native = math_exp; break; + case AsmJSMathBuiltin_log: native = math_log; break; + case AsmJSMathBuiltin_pow: native = js_math_pow; break; + case AsmJSMathBuiltin_sqrt: native = js_math_sqrt; break; + case AsmJSMathBuiltin_abs: native = js_math_abs; break; + case AsmJSMathBuiltin_atan2: native = math_atan2; break; + case AsmJSMathBuiltin_imul: native = math_imul; break; + } + + if (!IsNativeFunction(v, native)) + return LinkFail(cx, "bad Math.* builtin"); + + return true; +} + +static bool +ValidateGlobalConstant(JSContext *cx, AsmJSModule::Global global, HandleValue globalVal) +{ + RootedPropertyName field(cx, global.constantName()); + RootedValue v(cx); + if (!GetProperty(cx, globalVal, field, &v)) + return false; + + if (!v.isNumber()) + return LinkFail(cx, "global constant value needs to be a number"); + + // NaN != NaN + if (MOZ_DOUBLE_IS_NaN(global.constantValue())) { + if (!MOZ_DOUBLE_IS_NaN(v.toNumber())) + return LinkFail(cx, "global constant value needs to be NaN"); + } else { + if (v.toNumber() != global.constantValue()) + return LinkFail(cx, "global constant value mismatch"); + } + + return true; +} + +static bool +DynamicallyLinkModule(JSContext *cx, StackFrame *fp, HandleObject moduleObj) +{ + AsmJSModule &module = AsmJSModuleObjectToModule(moduleObj); + if (module.isLinked()) + return LinkFail(cx, "As a temporary limitation, modules cannot be linked more than " + "once. This limitation should be removed in a future release. To " + "work around this, compile a second module (e.g., using the " + "Function constructor)."); + + RootedValue globalVal(cx, UndefinedValue()); + if (fp->numActualArgs() > 0) + globalVal = fp->unaliasedActual(0); + + RootedValue importVal(cx, UndefinedValue()); + if (fp->numActualArgs() > 1) + importVal = fp->unaliasedActual(1); + + RootedValue bufferVal(cx, UndefinedValue()); + if (fp->numActualArgs() > 2) + bufferVal = fp->unaliasedActual(2); + + Rooted heap(cx); + if (module.hasArrayView()) { + if (!IsTypedArrayBuffer(bufferVal)) + return LinkFail(cx, "bad ArrayBuffer argument"); + + heap = &bufferVal.toObject().asArrayBuffer(); + + if (!IsPowerOfTwo(heap->byteLength()) || heap->byteLength() < AsmJSAllocationGranularity) + return LinkFail(cx, "ArrayBuffer byteLength must be a power of two greater than 4096"); + + if (!ArrayBufferObject::prepareForAsmJS(cx, heap)) + return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use"); + +#if defined(JS_CPU_X86) + void *heapOffset = (void*)heap->dataPointer(); + void *heapLength = (void*)heap->byteLength(); + uint8_t *code = module.functionCode(); + for (unsigned i = 0; i < module.numHeapAccesses(); i++) { + const AsmJSHeapAccess &access = module.heapAccess(i); + JSC::X86Assembler::setPointer(access.patchLengthAt(code), heapLength); + JSC::X86Assembler::setPointer(access.patchOffsetAt(code), heapOffset); + } +#endif + } + + AutoObjectVector ffis(cx); + if (!ffis.resize(module.numFFIs())) + return false; + + for (unsigned i = 0; i < module.numGlobals(); i++) { + AsmJSModule::Global global = module.global(i); + switch (global.which()) { + case AsmJSModule::Global::Variable: + if (!ValidateGlobalVariable(cx, module, global, importVal)) + return false; + break; + case AsmJSModule::Global::FFI: + if (!ValidateFFI(cx, global, importVal, &ffis)) + return false; + break; + case AsmJSModule::Global::ArrayView: + if (!ValidateArrayView(cx, global, globalVal, bufferVal)) + return false; + break; + case AsmJSModule::Global::MathBuiltin: + if (!ValidateMathBuiltin(cx, global, globalVal)) + return false; + break; + case AsmJSModule::Global::Constant: + if (!ValidateGlobalConstant(cx, global, globalVal)) + return false; + break; + } + } + + for (unsigned i = 0; i < module.numExits(); i++) + module.exitIndexToGlobalDatum(i).fun = ffis[module.exit(i).ffiIndex()]->toFunction(); + + module.setIsLinked(heap); + return true; +} + +AsmJSActivation::AsmJSActivation(JSContext *cx, const AsmJSModule &module, unsigned entryIndex) + : cx_(cx), + module_(module), + entryIndex_(entryIndex), + errorRejoinSP_(NULL), + profiler_(NULL), + resumePC_(NULL) +{ + if (cx->runtime->spsProfiler.enabled()) { + profiler_ = &cx->runtime->spsProfiler; + JSFunction *fun = module_.exportedFunction(entryIndex_).unclonedFunObj(); + profiler_->enter(cx_, fun->nonLazyScript(), fun); + } + + prev_ = cx_->runtime->mainThread.asmJSActivationStack_; + + PerThreadData::AsmJSActivationStackLock lock(cx_->runtime->mainThread); + cx_->runtime->mainThread.asmJSActivationStack_ = this; +} + +AsmJSActivation::~AsmJSActivation() +{ + if (profiler_) { + JSFunction *fun = module_.exportedFunction(entryIndex_).unclonedFunObj(); + profiler_->exit(cx_, fun->nonLazyScript(), fun); + } + + JS_ASSERT(cx_->runtime->mainThread.asmJSActivationStack_ == this); + + PerThreadData::AsmJSActivationStackLock lock(cx_->runtime->mainThread); + cx_->runtime->mainThread.asmJSActivationStack_ = prev_; +} + +static const unsigned ASM_MODULE_SLOT = 0; +static const unsigned ASM_EXPORT_INDEX_SLOT = 1; + +static JSBool +CallAsmJS(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs callArgs = CallArgsFromVp(argc, vp); + RootedFunction callee(cx, callArgs.callee().toFunction()); + + // An asm.js function stores, in its extended slots: + // - a pointer to the module from which it was returned + // - its index in the ordered list of exported functions + RootedObject moduleObj(cx, &callee->getExtendedSlot(ASM_MODULE_SLOT).toObject()); + const AsmJSModule &module = AsmJSModuleObjectToModule(moduleObj); + + // An exported function points to the code as well as the exported + // function's signature, which implies the dynamic coercions performed on + // the arguments. + unsigned exportIndex = callee->getExtendedSlot(ASM_EXPORT_INDEX_SLOT).toInt32(); + const AsmJSModule::ExportedFunction &func = module.exportedFunction(exportIndex); + + // The calling convention for an external call into asm.js is to pass an + // array of 8-byte values where each value contains either a coerced int32 + // (in the low word) or double value, with the coercions specified by the + // asm.js signature. The external entry point unpacks this array into the + // system-ABI-specified registers and stack memory and then calls into the + // internal entry point. The return value is stored in the first element of + // the array (which, therefore, must have length >= 1). + + Vector coercedArgs(cx); + if (!coercedArgs.resize(Max(1, func.numArgs()))) + return false; + + RootedValue v(cx); + for (unsigned i = 0; i < func.numArgs(); ++i) { + v = i < callArgs.length() ? callArgs[i] : UndefinedValue(); + switch (func.argCoercion(i)) { + case AsmJS_ToInt32: + if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i])) + return false; + break; + case AsmJS_ToNumber: + if (!ToNumber(cx, v, (double*)&coercedArgs[i])) + return false; + break; + } + } + + { + AsmJSActivation activation(cx, module, exportIndex); + + // Call into generated code. + if (!func.code()(coercedArgs.begin())) + return false; + } + + switch (func.returnType()) { + case AsmJSModule::Return_Void: + callArgs.rval().set(UndefinedValue()); + break; + case AsmJSModule::Return_Int32: + callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0])); + break; + case AsmJSModule::Return_Double: + callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0])); + break; + } + + return true; +} + +static JSFunction * +NewExportedFunction(JSContext *cx, const AsmJSModule::ExportedFunction &func, + HandleObject moduleObj, unsigned exportIndex) +{ + RootedPropertyName name(cx, func.name()); + JSFunction *fun = NewFunction(cx, NullPtr(), CallAsmJS, func.numArgs(), + JSFunction::NATIVE_FUN, cx->global(), name, + JSFunction::ExtendedFinalizeKind); + if (!fun) + return NULL; + + fun->setExtendedSlot(ASM_MODULE_SLOT, ObjectValue(*moduleObj)); + fun->setExtendedSlot(ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex)); + return fun; +} + +bool +js::LinkAsmJS(JSContext *cx, StackFrame *fp, MutableHandleValue rval) +{ + RootedObject moduleObj(cx, fp->fun()->nonLazyScript()->asmJS); + const AsmJSModule &module = AsmJSModuleObjectToModule(moduleObj); + + if (!DynamicallyLinkModule(cx, fp, moduleObj)) + return !cx->isExceptionPending(); + + if (module.numExportedFunctions() == 1) { + const AsmJSModule::ExportedFunction &func = module.exportedFunction(0); + if (!func.maybeFieldName()) { + RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, 0)); + if (!fun) + return false; + + rval.set(ObjectValue(*fun)); + return true; + } + } + + gc::AllocKind allocKind = gc::GetGCObjectKind(module.numExportedFunctions()); + RootedObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, allocKind)); + if (!obj) + return false; + + for (unsigned i = 0; i < module.numExportedFunctions(); i++) { + const AsmJSModule::ExportedFunction &func = module.exportedFunction(i); + + RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i)); + if (!fun) + return false; + + JS_ASSERT(func.maybeFieldName() != NULL); + RootedId id(cx, NameToId(func.maybeFieldName())); + RootedValue val(cx, ObjectValue(*fun)); + if (!DefineNativeProperty(cx, obj, id, val, NULL, NULL, JSPROP_ENUMERATE, 0, 0)) + return false; + } + + rval.set(ObjectValue(*obj)); + return true; +} diff --git a/js/src/ion/AsmJSModule.h b/js/src/ion/AsmJSModule.h new file mode 100644 index 00000000000..608f52af6fb --- /dev/null +++ b/js/src/ion/AsmJSModule.h @@ -0,0 +1,549 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et 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/. */ + +#if !defined(jsion_asmjsmodule_h__) +#define jsion_asmjsmodule_h__ + +#include "gc/Marking.h" +#include "ion/RegisterSets.h" + +#include "jstypedarrayinlines.h" + +namespace js { + +// The basis of the asm.js type system is the EcmaScript-defined coercions +// ToInt32 and ToNumber. +enum AsmJSCoercion +{ + AsmJS_ToInt32, + AsmJS_ToNumber +}; + +// The asm.js spec recognizes this set of builtin Math functions. +enum AsmJSMathBuiltin +{ + AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan, + AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan, + AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp, + AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt, + AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul +}; + +// An asm.js module represents the collection of functions nested inside a +// single outer "use asm" function. For example, this asm.js module: +// function() { "use asm"; function f() {} function g() {} return f } +// contains the functions 'f' and 'g'. +// +// An asm.js module contains both the jit-code produced by compiling all the +// functions in the module as well all the data required to perform the +// link-time validation step in the asm.js spec. +// +// NB: this means that AsmJSModule must be GC-safe. +class AsmJSModule +{ + public: + class Global + { + public: + enum Which { Variable, FFI, ArrayView, MathBuiltin, Constant }; + enum VarInitKind { InitConstant, InitImport }; + + private: + Which which_; + union { + struct { + uint32_t index_; + VarInitKind initKind_; + union { + Value constant_; + AsmJSCoercion coercion_; + } init; + } var; + uint32_t ffiIndex_; + ArrayBufferView::ViewType viewType_; + AsmJSMathBuiltin mathBuiltin_; + double constantValue_; + } u; + HeapPtrPropertyName name_; + + friend class AsmJSModule; + Global(Which which) : which_(which) {} + + void trace(JSTracer *trc) { + if (name_) + MarkString(trc, &name_, "asm.js global name"); + } + + public: + Which which() const { + return which_; + } + uint32_t varIndex() const { + JS_ASSERT(which_ == Variable); + return u.var.index_; + } + VarInitKind varInitKind() const { + JS_ASSERT(which_ == Variable); + return u.var.initKind_; + } + const Value &varInitConstant() const { + JS_ASSERT(which_ == Variable); + JS_ASSERT(u.var.initKind_ == InitConstant); + return u.var.init.constant_; + } + AsmJSCoercion varImportCoercion() const { + JS_ASSERT(which_ == Variable); + JS_ASSERT(u.var.initKind_ == InitImport); + return u.var.init.coercion_; + } + PropertyName *varImportField() const { + JS_ASSERT(which_ == Variable); + JS_ASSERT(u.var.initKind_ == InitImport); + return name_; + } + PropertyName *ffiField() const { + JS_ASSERT(which_ == FFI); + return name_; + } + uint32_t ffiIndex() const { + JS_ASSERT(which_ == FFI); + return u.ffiIndex_; + } + PropertyName *viewName() const { + JS_ASSERT(which_ == ArrayView); + return name_; + } + ArrayBufferView::ViewType viewType() const { + JS_ASSERT(which_ == ArrayView); + return u.viewType_; + } + PropertyName *mathName() const { + JS_ASSERT(which_ == MathBuiltin); + return name_; + } + AsmJSMathBuiltin mathBuiltin() const { + JS_ASSERT(which_ == MathBuiltin); + return u.mathBuiltin_; + } + PropertyName *constantName() const { + JS_ASSERT(which_ == Constant); + return name_; + } + double constantValue() const { + JS_ASSERT(which_ == Constant); + return u.constantValue_; + } + }; + + class Exit + { + unsigned ffiIndex_; + union { + unsigned codeOffset_; + uint8_t *code_; + } u; + + public: + Exit(unsigned ffiIndex) + : ffiIndex_(ffiIndex) + { + u.codeOffset_ = 0; + } + unsigned ffiIndex() const { + return ffiIndex_; + } + void initCodeOffset(unsigned off) { + JS_ASSERT(!u.codeOffset_); + u.codeOffset_ = off; + } + void patch(uint8_t *baseAddress) { + u.code_ = baseAddress + u.codeOffset_; + } + uint8_t *code() const { + return u.code_; + } + }; + + typedef int32_t (*CodePtr)(uint64_t *args); + + typedef Vector ArgCoercionVector; + + enum ReturnType { Return_Int32, Return_Double, Return_Void }; + + class ExportedFunction + { + public: + + private: + + HeapPtrFunction fun_; + HeapPtrPropertyName maybeFieldName_; + ArgCoercionVector argCoercions_; + ReturnType returnType_; + bool hasCodePtr_; + union { + unsigned codeOffset_; + CodePtr code_; + } u; + + friend class AsmJSModule; + + ExportedFunction(JSFunction *fun, + PropertyName *maybeFieldName, + MoveRef argCoercions, + ReturnType returnType) + : fun_(fun), + maybeFieldName_(maybeFieldName), + argCoercions_(argCoercions), + returnType_(returnType), + hasCodePtr_(false) + { + u.codeOffset_ = 0; + } + + void trace(JSTracer *trc) { + MarkObject(trc, &fun_, "asm.js export name"); + if (maybeFieldName_) + MarkString(trc, &maybeFieldName_, "asm.js export field"); + } + + public: + ExportedFunction(MoveRef rhs) + : fun_(rhs->fun_), + maybeFieldName_(rhs->maybeFieldName_), + argCoercions_(Move(rhs->argCoercions_)), + returnType_(rhs->returnType_), + hasCodePtr_(rhs->hasCodePtr_), + u(rhs->u) + {} + + void initCodeOffset(unsigned off) { + JS_ASSERT(!hasCodePtr_); + JS_ASSERT(!u.codeOffset_); + u.codeOffset_ = off; + } + void patch(uint8_t *baseAddress) { + JS_ASSERT(!hasCodePtr_); + JS_ASSERT(u.codeOffset_); + hasCodePtr_ = true; + u.code_ = JS_DATA_TO_FUNC_PTR(CodePtr, baseAddress + u.codeOffset_); + } + + PropertyName *name() const { + return fun_->name(); + } + JSFunction *unclonedFunObj() const { + return fun_; + } + PropertyName *maybeFieldName() const { + return maybeFieldName_; + } + unsigned numArgs() const { + return argCoercions_.length(); + } + AsmJSCoercion argCoercion(unsigned i) const { + return argCoercions_[i]; + } + ReturnType returnType() const { + return returnType_; + } + CodePtr code() const { + JS_ASSERT(hasCodePtr_); + return u.code_; + } + }; + + private: + typedef Vector ExportedFunctionVector; + typedef Vector GlobalVector; + typedef Vector ExitVector; + typedef Vector HeapAccessVector; + + GlobalVector globals_; + ExitVector exits_; + ExportedFunctionVector exports_; + HeapAccessVector heapAccesses_; + uint32_t numGlobalVars_; + uint32_t numFFIs_; + uint32_t numFuncPtrTableElems_; + bool hasArrayView_; + + ScopedReleasePtr codePool_; + uint8_t * code_; + uint8_t * operationCallbackExit_; + size_t functionBytes_; + size_t codeBytes_; + size_t totalBytes_; + + bool linked_; + HeapPtr maybeHeap_; + + uint8_t *globalData() const { + JS_ASSERT(code_); + return code_ + codeBytes_; + } + + public: + AsmJSModule() + : numGlobalVars_(0), + numFFIs_(0), + numFuncPtrTableElems_(0), + hasArrayView_(false), + code_(NULL), + operationCallbackExit_(NULL), + functionBytes_(0), + codeBytes_(0), + totalBytes_(0), + linked_(false) + {} + + void trace(JSTracer *trc) { + for (unsigned i = 0; i < globals_.length(); i++) + globals_[i].trace(trc); + for (unsigned i = 0; i < exports_.length(); i++) + exports_[i].trace(trc); + for (unsigned i = 0; i < exits_.length(); i++) { + if (exitIndexToGlobalDatum(i).fun) + MarkObject(trc, &exitIndexToGlobalDatum(i).fun, "asm.js imported function"); + } + if (maybeHeap_) + MarkObject(trc, &maybeHeap_, "asm.js heap"); + } + + bool addGlobalVarInitConstant(const Value &v, uint32_t *globalIndex) { + if (numGlobalVars_ == UINT32_MAX) + return false; + Global g(Global::Variable); + g.u.var.initKind_ = Global::InitConstant; + g.u.var.init.constant_ = v; + g.u.var.index_ = *globalIndex = numGlobalVars_++; + return globals_.append(g); + } + bool addGlobalVarImport(PropertyName *fieldName, AsmJSCoercion coercion, uint32_t *globalIndex) { + Global g(Global::Variable); + g.u.var.initKind_ = Global::InitImport; + g.u.var.init.coercion_ = coercion; + g.u.var.index_ = *globalIndex = numGlobalVars_++; + g.name_ = fieldName; + return globals_.append(g); + } + bool incrementNumFuncPtrTableElems(uint32_t numElems) { + if (UINT32_MAX - numFuncPtrTableElems_ < numElems) + return false; + numFuncPtrTableElems_ += numElems; + return true; + } + bool addFFI(PropertyName *field, uint32_t *ffiIndex) { + if (numFFIs_ == UINT32_MAX) + return false; + Global g(Global::FFI); + g.u.ffiIndex_ = *ffiIndex = numFFIs_++; + g.name_ = field; + return globals_.append(g); + } + bool addArrayView(ArrayBufferView::ViewType vt, PropertyName *field) { + hasArrayView_ = true; + Global g(Global::ArrayView); + g.u.viewType_ = vt; + g.name_ = field; + return globals_.append(g); + } + bool addMathBuiltin(AsmJSMathBuiltin mathBuiltin, PropertyName *field) { + Global g(Global::MathBuiltin); + g.u.mathBuiltin_ = mathBuiltin; + g.name_ = field; + return globals_.append(g); + } + bool addGlobalConstant(double value, PropertyName *fieldName) { + Global g(Global::Constant); + g.u.constantValue_ = value; + g.name_ = fieldName; + return globals_.append(g); + } + bool addExit(unsigned ffiIndex, unsigned *exitIndex) { + *exitIndex = unsigned(exits_.length()); + return exits_.append(Exit(ffiIndex)); + } + + bool addExportedFunction(RawFunction fun, PropertyName *maybeFieldName, + MoveRef argCoercions, ReturnType returnType) + { + ExportedFunction func(fun, maybeFieldName, argCoercions, returnType); + return exports_.append(Move(func)); + } + unsigned numExportedFunctions() const { + return exports_.length(); + } + const ExportedFunction &exportedFunction(unsigned i) const { + return exports_[i]; + } + ExportedFunction &exportedFunction(unsigned i) { + return exports_[i]; + } + bool hasArrayView() const { + return hasArrayView_; + } + unsigned numFFIs() const { + return numFFIs_; + } + unsigned numGlobalVars() const { + return numGlobalVars_; + } + unsigned numGlobals() const { + return globals_.length(); + } + Global global(unsigned i) const { + return globals_[i]; + } + unsigned numFuncPtrTableElems() const { + return numFuncPtrTableElems_; + } + unsigned numExits() const { + return exits_.length(); + } + Exit &exit(unsigned i) { + return exits_[i]; + } + const Exit &exit(unsigned i) const { + return exits_[i]; + } + + // An Exit holds bookkeeping information about an exit; the ExitDatum + // struct overlays the actual runtime data stored in the global data + // section. + struct ExitDatum + { + uint8_t *exit; + HeapPtrFunction fun; + }; + + // Global data section + // + // The global data section is placed after the executable code (i.e., at + // offset codeBytes_) in the module's linear allocation. The global data + // are laid out in this order: + // 0. a pointer/descriptor for the heap that was linked to the module + // 1. global variable state (elements are sizeof(uint64_t)) + // 2. function-pointer table elements (elements are sizeof(void*)) + // 3. exits (elements are sizeof(ExitDatum)) + // + // NB: The list of exits is extended while emitting function bodies and + // thus exits must be at the end of the list to avoid invalidating indices. + size_t globalDataBytes() const { + return sizeof(void*) + + numGlobalVars_ * sizeof(uint64_t) + + numFuncPtrTableElems_ * sizeof(void*) + + exits_.length() * sizeof(ExitDatum); + } + unsigned heapOffset() const { + return 0; + } + uint8_t *&heapDatum() const { + return *(uint8_t**)(globalData() + heapOffset()); + } + unsigned globalVarIndexToGlobalDataOffset(unsigned i) const { + JS_ASSERT(i < numGlobalVars_); + return sizeof(void*) + + i * sizeof(uint64_t); + } + void *globalVarIndexToGlobalDatum(unsigned i) const { + return (void *)(globalData() + globalVarIndexToGlobalDataOffset(i)); + } + unsigned funcPtrIndexToGlobalDataOffset(unsigned i) const { + return sizeof(void*) + + numGlobalVars_ * sizeof(uint64_t) + + i * sizeof(void*); + } + void *&funcPtrIndexToGlobalDatum(unsigned i) const { + return *(void **)(globalData() + funcPtrIndexToGlobalDataOffset(i)); + } + unsigned exitIndexToGlobalDataOffset(unsigned exitIndex) const { + JS_ASSERT(exitIndex < exits_.length()); + return sizeof(void*) + + numGlobalVars_ * sizeof(uint64_t) + + numFuncPtrTableElems_ * sizeof(void*) + + exitIndex * sizeof(ExitDatum); + } + ExitDatum &exitIndexToGlobalDatum(unsigned exitIndex) const { + return *(ExitDatum *)(globalData() + exitIndexToGlobalDataOffset(exitIndex)); + } + + void setFunctionBytes(size_t functionBytes) { + JS_ASSERT(functionBytes % gc::PageSize == 0); + functionBytes_ = functionBytes; + } + size_t functionBytes() const { + JS_ASSERT(functionBytes_); + JS_ASSERT(functionBytes_ % gc::PageSize == 0); + return functionBytes_; + } + + bool addHeapAccesses(const Vector &accesses) { + if (!heapAccesses_.reserve(heapAccesses_.length() + accesses.length())) + return false; + for (size_t i = 0; i < accesses.length(); i++) + heapAccesses_.infallibleAppend(accesses[i]); + return true; + } + unsigned numHeapAccesses() const { + return heapAccesses_.length(); + } + ion::AsmJSHeapAccess &heapAccess(unsigned i) { + return heapAccesses_[i]; + } + const ion::AsmJSHeapAccess &heapAccess(unsigned i) const { + return heapAccesses_[i]; + } + + void takeOwnership(JSC::ExecutablePool *pool, uint8_t *code, size_t codeBytes, size_t totalBytes) { + JS_ASSERT(uintptr_t(code) % gc::PageSize == 0); + codePool_ = pool; + code_ = code; + codeBytes_ = codeBytes; + totalBytes_ = totalBytes; + } + uint8_t *functionCode() const { + JS_ASSERT(code_); + JS_ASSERT(uintptr_t(code_) % gc::PageSize == 0); + return code_; + } + + void setOperationCallbackExit(uint8_t *ptr) { + operationCallbackExit_ = ptr; + } + uint8_t *operationCallbackExit() const { + return operationCallbackExit_; + } + + void setIsLinked(Handle maybeHeap) { + JS_ASSERT(!linked_); + linked_ = true; + maybeHeap_ = maybeHeap; + heapDatum() = maybeHeap_ ? maybeHeap_->dataPointer() : NULL; + } + bool isLinked() const { + return linked_; + } + uint8_t *maybeHeap() const { + JS_ASSERT(linked_); + return heapDatum(); + } + size_t heapLength() const { + JS_ASSERT(linked_); + return maybeHeap_ ? maybeHeap_->byteLength() : 0; + } +}; + +// The AsmJSModule C++ object is held by a JSObject that takes care of calling +// 'trace' and the destructor on finalization. +extern AsmJSModule & +AsmJSModuleObjectToModule(JSObject *obj); + +} // namespace js + +#endif // jsion_asmjsmodule_h__ + diff --git a/js/src/ion/AsmJSSignalHandlers.cpp b/js/src/ion/AsmJSSignalHandlers.cpp new file mode 100644 index 00000000000..45eeac98e4f --- /dev/null +++ b/js/src/ion/AsmJSSignalHandlers.cpp @@ -0,0 +1,563 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jscntxt.h" + +#include "jstypedarrayinlines.h" + +#include "ion/AsmJS.h" +#include "ion/AsmJSModule.h" +#include "assembler/assembler/MacroAssembler.h" + +using namespace js; +using namespace js::ion; + +#ifdef JS_ASMJS + +// Prevent races trying to install the signal handlers. +#ifdef JS_THREADSAFE +# include "jslock.h" + +class SignalMutex +{ + PRLock *mutex_; + + public: + SignalMutex() { + mutex_ = PR_NewLock(); + if (!mutex_) + MOZ_CRASH(); + } + ~SignalMutex() { + PR_DestroyLock(mutex_); + } + class Lock { + static bool sHandlersInstalled; + public: + Lock(); + ~Lock(); + bool handlersInstalled() const { return sHandlersInstalled; } + void setHandlersInstalled() { sHandlersInstalled = true; } + }; +} signalMutex; + +bool SignalMutex::Lock::sHandlersInstalled = false; + +SignalMutex::Lock::Lock() +{ + PR_Lock(signalMutex.mutex_); +} + +SignalMutex::Lock::~Lock() +{ + PR_Unlock(signalMutex.mutex_); +} +#else +struct SignalMutex +{ + class Lock { + static bool sHandlersInstalled; + public: + Lock() { (void)this; } + bool handlersInstalled() const { return sHandlersInstalled; } + void setHandlersInstalled() { sHandlersInstalled = true; } + }; +}; + +bool SignalMutex::Lock::sHandlersInstalled = false; +#endif + +static AsmJSActivation * +InnermostAsmJSActivation() +{ + PerThreadData *threadData = TlsPerThreadData.get(); + if (!threadData) + return NULL; + + return threadData->asmJSActivationStackFromOwnerThread(); +} + +static bool +PCIsInModule(const AsmJSModule &module, void *pc) +{ + uint8_t *code = module.functionCode(); + return pc >= code && pc < (code + module.functionBytes()); +} + +# if defined(JS_CPU_X64) +template +static void +SetXMMRegToNaN(bool isFloat32, T *xmm_reg) +{ + if (isFloat32) { + JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float)); + float *floats = reinterpret_cast(xmm_reg); + floats[0] = js_NaN; + floats[1] = 0; + floats[2] = 0; + floats[3] = 0; + } else { + JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double)); + double *dbls = reinterpret_cast(xmm_reg); + dbls[0] = js_NaN; + dbls[1] = 0; + } +} + +// Perform a binary search on the projected offsets of the known heap accesses +// in the module. +static const AsmJSHeapAccess * +LookupHeapAccess(const AsmJSModule &module, uint8_t *pc) +{ + JS_ASSERT(PCIsInModule(module, pc)); + size_t targetOffset = pc - module.functionCode(); + + if (module.numHeapAccesses() == 0) + return NULL; + + size_t low = 0; + size_t high = module.numHeapAccesses() - 1; + while (high - low >= 2) { + size_t mid = low + (high - low) / 2; + uint32_t midOffset = module.heapAccess(mid).offset(); + if (targetOffset == midOffset) + return &module.heapAccess(mid); + if (targetOffset < midOffset) + high = mid; + else + low = mid; + } + if (targetOffset == module.heapAccess(low).offset()) + return &module.heapAccess(low); + if (targetOffset == module.heapAccess(high).offset()) + return &module.heapAccess(high); + + return NULL; +} +# endif + +# if defined(XP_WIN) +# include "jswin.h" + +static uint8_t ** +ContextToPC(PCONTEXT context) +{ +# if defined(JS_CPU_X64) + JS_STATIC_ASSERT(sizeof(context->Rip) == sizeof(void*)); + return reinterpret_cast(&context->Rip); +# else + JS_STATIC_ASSERT(sizeof(context->Eip) == sizeof(void*)); + return reinterpret_cast(&context->Eip); +# endif +} + +# if defined(JS_CPU_X64) +static void +SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg) +{ + if (reg.isFloat()) { + switch (reg.fpu().code()) { + case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &context->Xmm0); break; + case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &context->Xmm1); break; + case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &context->Xmm2); break; + case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &context->Xmm3); break; + case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &context->Xmm4); break; + case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &context->Xmm5); break; + case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &context->Xmm6); break; + case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &context->Xmm7); break; + case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &context->Xmm8); break; + case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &context->Xmm9); break; + case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &context->Xmm10); break; + case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &context->Xmm11); break; + case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &context->Xmm12); break; + case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &context->Xmm13); break; + case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &context->Xmm14); break; + case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &context->Xmm15); break; + default: MOZ_CRASH(); + } + } else { + switch (reg.gpr().code()) { + case JSC::X86Registers::eax: context->Rax = 0; break; + case JSC::X86Registers::ecx: context->Rcx = 0; break; + case JSC::X86Registers::edx: context->Rdx = 0; break; + case JSC::X86Registers::ebx: context->Rbx = 0; break; + case JSC::X86Registers::esp: context->Rsp = 0; break; + case JSC::X86Registers::ebp: context->Rbp = 0; break; + case JSC::X86Registers::esi: context->Rsi = 0; break; + case JSC::X86Registers::edi: context->Rdi = 0; break; + case JSC::X86Registers::r8: context->R8 = 0; break; + case JSC::X86Registers::r9: context->R9 = 0; break; + case JSC::X86Registers::r10: context->R10 = 0; break; + case JSC::X86Registers::r11: context->R11 = 0; break; + case JSC::X86Registers::r12: context->R12 = 0; break; + case JSC::X86Registers::r13: context->R13 = 0; break; + case JSC::X86Registers::r14: context->R14 = 0; break; + case JSC::X86Registers::r15: context->R15 = 0; break; + default: MOZ_CRASH(); + } + } +} +# endif + +static bool +HandleException(PEXCEPTION_POINTERS exception) +{ + EXCEPTION_RECORD *record = exception->ExceptionRecord; + CONTEXT *context = exception->ContextRecord; + + if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) + return false; + + AsmJSActivation *activation = InnermostAsmJSActivation(); + if (!activation) + return false; + + uint8_t **ppc = ContextToPC(context); + uint8_t *pc = *ppc; + JS_ASSERT(pc == record->ExceptionAddress); + + const AsmJSModule &module = activation->module(); + if (!PCIsInModule(module, pc)) + return false; + + if (record->NumberParameters < 2) + return false; + + void *faultingAddress = (void*)record->ExceptionInformation[1]; + + // If we faulted trying to execute code in 'module', this must be an + // operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect + // execution to a trampoline which will call js_HandleExecutionInterrupt. + // The trampoline will jump to activation->resumePC if execution isn't + // interrupted. + if (PCIsInModule(module, faultingAddress)) { + activation->setResumePC(pc); + *ppc = module.operationCallbackExit(); + DWORD oldProtect; + if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_EXECUTE, &oldProtect)) + MOZ_CRASH(); + return true; + } + +# if defined(JS_CPU_X64) + // These checks aren't necessary, but, since we can, check anyway to make + // sure we aren't covering up a real bug. + if (!module.maybeHeap() || + faultingAddress < module.maybeHeap() || + faultingAddress >= module.maybeHeap() + AsmJSBufferProtectedSize) + { + return false; + } + + const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); + if (!heapAccess) + return false; + + // Also not necessary, but, since we can, do. + if (heapAccess->isLoad() != !record->ExceptionInformation[0]) + return false; + + // We now know that this is an out-of-bounds access made by an asm.js + // load/store that we should handle. If this is a load, assign the + // JS-defined result value to the destination register (ToInt32(undefined) + // or ToNumber(undefined), determined by the type of the destination + // register) and set the PC to the next op. Upon return from the handler, + // execution will resume at this next PC. + if (heapAccess->isLoad()) + SetRegisterToCoercedUndefined(context, heapAccess->isFloat32Load(), heapAccess->loadedReg()); + *ppc += heapAccess->opLength(); + return true; +# else + return false; +# endif +} + +static LONG WINAPI +AsmJSExceptionHandler(LPEXCEPTION_POINTERS exception) +{ + if (HandleException(exception)) + return EXCEPTION_CONTINUE_EXECUTION; + + // No need to worry about calling other handlers, the OS does this for us. + return EXCEPTION_CONTINUE_SEARCH; +} + +# else // If not Windows, assume Unix +# include +# include + +// Unfortunately, we still need OS-specific code to read/write to the thread +// state via the mcontext_t. +# if defined(__linux__) +static uint8_t ** +ContextToPC(mcontext_t &context) +{ +# if defined(JS_CPU_X86) + JS_STATIC_ASSERT(sizeof(context.gregs[REG_EIP]) == sizeof(void*)); + return reinterpret_cast(&context.gregs[REG_EIP]); +# else + JS_STATIC_ASSERT(sizeof(context.gregs[REG_RIP]) == sizeof(void*)); + return reinterpret_cast(&context.gregs[REG_RIP]); +# endif +} + +# if defined(JS_CPU_X64) +static void +SetRegisterToCoercedUndefined(mcontext_t &context, bool isFloat32, AnyRegister reg) +{ + if (reg.isFloat()) { + switch (reg.fpu().code()) { + case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[0]); break; + case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[1]); break; + case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[2]); break; + case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[3]); break; + case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[4]); break; + case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[5]); break; + case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[6]); break; + case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[7]); break; + case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[8]); break; + case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[9]); break; + case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[10]); break; + case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[11]); break; + case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[12]); break; + case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[13]); break; + case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[14]); break; + case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &context.fpregs->_xmm[15]); break; + default: MOZ_CRASH(); + } + } else { + switch (reg.gpr().code()) { + case JSC::X86Registers::eax: context.gregs[REG_RAX] = 0; break; + case JSC::X86Registers::ecx: context.gregs[REG_RCX] = 0; break; + case JSC::X86Registers::edx: context.gregs[REG_RDX] = 0; break; + case JSC::X86Registers::ebx: context.gregs[REG_RBX] = 0; break; + case JSC::X86Registers::esp: context.gregs[REG_RSP] = 0; break; + case JSC::X86Registers::ebp: context.gregs[REG_RBP] = 0; break; + case JSC::X86Registers::esi: context.gregs[REG_RSI] = 0; break; + case JSC::X86Registers::edi: context.gregs[REG_RDI] = 0; break; + case JSC::X86Registers::r8: context.gregs[REG_R8] = 0; break; + case JSC::X86Registers::r9: context.gregs[REG_R9] = 0; break; + case JSC::X86Registers::r10: context.gregs[REG_R10] = 0; break; + case JSC::X86Registers::r11: context.gregs[REG_R11] = 0; break; + case JSC::X86Registers::r12: context.gregs[REG_R12] = 0; break; + case JSC::X86Registers::r13: context.gregs[REG_R13] = 0; break; + case JSC::X86Registers::r14: context.gregs[REG_R14] = 0; break; + case JSC::X86Registers::r15: context.gregs[REG_R15] = 0; break; + default: MOZ_CRASH(); + } + } +} +# endif +# elif defined(XP_MACOSX) +static uint8_t ** +ContextToPC(mcontext_t context) +{ +# if defined(JS_CPU_X86) + JS_STATIC_ASSERT(sizeof(context->__ss.__eip) == sizeof(void*)); + return reinterpret_cast(&context->__ss.__eip); +# else + JS_STATIC_ASSERT(sizeof(context->__ss.__rip) == sizeof(void*)); + return reinterpret_cast(&context->__ss.__rip); +# endif +} + +# if defined(JS_CPU_X64) +static void +SetRegisterToCoercedUndefined(mcontext_t &context, bool isFloat32, AnyRegister reg) +{ + if (reg.isFloat()) { + switch (reg.fpu().code()) { + case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm0); break; + case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm1); break; + case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm2); break; + case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm3); break; + case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm4); break; + case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm5); break; + case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm6); break; + case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm7); break; + case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm8); break; + case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm9); break; + case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm10); break; + case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm11); break; + case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm12); break; + case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm13); break; + case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm14); break; + case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm15); break; + default: MOZ_CRASH(); + } + } else { + switch (reg.gpr().code()) { + case JSC::X86Registers::eax: context->__ss.__rax = 0; break; + case JSC::X86Registers::ecx: context->__ss.__rcx = 0; break; + case JSC::X86Registers::edx: context->__ss.__rdx = 0; break; + case JSC::X86Registers::ebx: context->__ss.__rbx = 0; break; + case JSC::X86Registers::esp: context->__ss.__rsp = 0; break; + case JSC::X86Registers::ebp: context->__ss.__rbp = 0; break; + case JSC::X86Registers::esi: context->__ss.__rsi = 0; break; + case JSC::X86Registers::edi: context->__ss.__rdi = 0; break; + case JSC::X86Registers::r8: context->__ss.__r8 = 0; break; + case JSC::X86Registers::r9: context->__ss.__r9 = 0; break; + case JSC::X86Registers::r10: context->__ss.__r10 = 0; break; + case JSC::X86Registers::r11: context->__ss.__r11 = 0; break; + case JSC::X86Registers::r12: context->__ss.__r12 = 0; break; + case JSC::X86Registers::r13: context->__ss.__r13 = 0; break; + case JSC::X86Registers::r14: context->__ss.__r14 = 0; break; + case JSC::X86Registers::r15: context->__ss.__r15 = 0; break; + default: MOZ_CRASH(); + } + } +} +# endif +# endif // end of OS-specific mcontext accessors + +// Be very cautious and default to not handling; we don't want to accidentally +// silence real crashes from real bugs. +static bool +HandleSignal(int signum, siginfo_t *info, void *ctx) +{ + AsmJSActivation *activation = InnermostAsmJSActivation(); + if (!activation) + return false; + + mcontext_t &context = reinterpret_cast(ctx)->uc_mcontext; + uint8_t **ppc = ContextToPC(context); + uint8_t *pc = *ppc; + + const AsmJSModule &module = activation->module(); + if (!PCIsInModule(module, pc)) + return false; + + void *faultingAddress = info->si_addr; + + // If we faulted trying to execute code in 'module', this must be an + // operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect + // execution to a trampoline which will call js_HandleExecutionInterrupt. + // The trampoline will jump to activation->resumePC if execution isn't + // interrupted. + if (PCIsInModule(module, faultingAddress)) { + activation->setResumePC(pc); + *ppc = module.operationCallbackExit(); + mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC); + return true; + } + +# if defined(JS_CPU_X64) + // These checks aren't necessary, but, since we can, check anyway to make + // sure we aren't covering up a real bug. + if (!module.maybeHeap() || + faultingAddress < module.maybeHeap() || + faultingAddress >= module.maybeHeap() + AsmJSBufferProtectedSize) + { + return false; + } + + const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); + if (!heapAccess) + return false; + + // We now know that this is an out-of-bounds access made by an asm.js + // load/store that we should handle. If this is a load, assign the + // JS-defined result value to the destination register (ToInt32(undefined) + // or ToNumber(undefined), determined by the type of the destination + // register) and set the PC to the next op. Upon return from the handler, + // execution will resume at this next PC. + if (heapAccess->isLoad()) + SetRegisterToCoercedUndefined(context, heapAccess->isFloat32Load(), heapAccess->loadedReg()); + *ppc += heapAccess->opLength(); + return true; +# else + return false; +# endif +} + +static struct sigaction sPrevHandler; + +static void +AsmJSFaultHandler(int signum, siginfo_t *info, void *context) +{ + if (HandleSignal(signum, info, context)) + return; + + // This signal is not for any asm.js code we expect, so we need to forward + // the signal to the next handler. If there is no next handler (SIG_IGN or + // SIG_DFL), then it's time to crash. To do this, we set the signal back to + // it's previous disposition and return. This will cause the faulting op to + // be re-executed which will crash in the normal way. The advantage to + // doing this is that we remove ourselves from the crash stack which + // simplifies crash reports. Note: the order of these tests matter. + if (sPrevHandler.sa_flags & SA_SIGINFO) { + sPrevHandler.sa_sigaction(signum, info, context); + exit(signum); // backstop + } else if (sPrevHandler.sa_handler == SIG_DFL || sPrevHandler.sa_handler == SIG_IGN) { + sigaction(signum, &sPrevHandler, NULL); + } else { + sPrevHandler.sa_handler(signum); + exit(signum); // backstop + } +} +# endif +#endif // JS_ASMJS + +bool +EnsureAsmJSSignalHandlersInstalled() +{ +#if defined(JS_ASMJS) + SignalMutex::Lock lock; + if (lock.handlersInstalled()) + return true; + +#if defined(XP_WIN) + if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSExceptionHandler)) + return false; +#else + struct sigaction sigAction; + sigAction.sa_sigaction = &AsmJSFaultHandler; + sigemptyset(&sigAction.sa_mask); + sigAction.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sigAction, &sPrevHandler)) + return false; + if (sigaction(SIGBUS, &sigAction, &sPrevHandler)) + return false; +#endif + + lock.setHandlersInstalled(); +#endif + return true; +} + +// To interrupt execution of a JSRuntime, any thread may call +// JS_TriggerOperationCallback (JSRuntime::triggerOperationCallback from inside +// the engine). Normally, this sets some state that is polled at regular +// intervals (function prologues, loop headers), even from jit-code. For tight +// loops, this poses non-trivial overhead. For asm.js, we can do better: when +// another thread triggers the operation callback, we simply mprotect all of +// the innermost asm.js module activation's code. This will trigger a SIGSEGV, +// taking us into AsmJSFaultHandler. From there, we can manually redirect +// execution to call js_HandleExecutionInterrupt. The memory is un-protected +// from the signal handler after control flow is redirected. +void +js::TriggerOperationCallbackForAsmJSCode(JSRuntime *rt) +{ +#if defined(JS_ASMJS) + PerThreadData::AsmJSActivationStackLock lock(rt->mainThread); + + AsmJSActivation *activation = rt->mainThread.asmJSActivationStackFromAnyThread(); + if (!activation) + return; + + const AsmJSModule &module = activation->module(); + +# if defined(XP_WIN) + DWORD oldProtect; + if (!VirtualProtect(module.functionCode(), 4096, PAGE_NOACCESS, &oldProtect)) + MOZ_CRASH(); +# else + if (mprotect(module.functionCode(), module.functionBytes(), PROT_NONE)) + MOZ_CRASH(); +# endif +#endif +} diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index 401b8ed363a..a051cb84ac5 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -124,8 +124,8 @@ MNewStringObject::templateObj() const { return &templateObj_->asString(); } -CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph) - : CodeGeneratorSpecific(gen, graph) +CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) + : CodeGeneratorSpecific(gen, graph, masm) { } @@ -2046,6 +2046,9 @@ CodeGenerator::maybeCreateScriptCounts() CompileInfo *outerInfo = &gen->info(); RawScript script = outerInfo->script(); + if (!script) + return NULL; + if (cx->runtime->profilingScripts && !script->hasScriptCounts) { if (!script->initScriptCounts(cx)) return NULL; @@ -2858,6 +2861,16 @@ CodeGenerator::visitPowD(LPowD *ins) return true; } +bool +CodeGenerator::visitNegI(LNegI *ins) +{ + Register input = ToRegister(ins->input()); + JS_ASSERT(input == ToRegister(ins->output())); + + masm.neg32(input); + return true; +} + bool CodeGenerator::visitNegD(LNegD *ins) { @@ -3201,7 +3214,7 @@ CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir) JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); - Assembler::Condition cond = JSOpToCondition(op); + Assembler::Condition cond = JSOpToCondition(compareType, op); if (compareType == MCompare::Compare_Null) cond = masm.testNull(cond, value); else @@ -3267,7 +3280,7 @@ CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBran JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); - Assembler::Condition cond = JSOpToCondition(op); + Assembler::Condition cond = JSOpToCondition(compareType, op); if (compareType == MCompare::Compare_Null) cond = masm.testNull(cond, value); else @@ -4326,6 +4339,43 @@ CodeGenerator::visitGetArgument(LGetArgument *lir) return true; } +bool +CodeGenerator::generateAsmJS() +{ + // The caller (either another asm.js function or the external-entry + // trampoline) has placed all arguments in registers and on the stack + // according to the system ABI. The MAsmJSParameters which represent these + // parameters have been useFixed()ed to these ABI-specified positions. + // Thus, there is nothing special to do in the prologue except (possibly) + // bump the stack. + if (!generatePrologue()) + return false; + if (!generateBody()) + return false; + if (!generateEpilogue()) + return false; + if (!generateOutOfLineCode()) + return false; + + // The only remaining work needed to compile this function is to patch the + // switch-statement jump tables (the entries of the table need the absolute + // address of the cases). These table entries are accmulated as CodeLabels + // in the MacroAssembler's codeLabels_ list and processed all at once at in + // the "static-link" phase of module compilation. It is critical that there + // is nothing else to do after this point since the LifoAlloc memory + // holding the MIR graph is about to be popped and reused. In particular, + // every step in CodeGenerator::link must be a nop, as asserted here: + JS_ASSERT(snapshots_.size() == 0); + JS_ASSERT(bailouts_.empty()); + JS_ASSERT(graph.numConstants() == 0); + JS_ASSERT(safepointIndices_.empty()); + JS_ASSERT(osiIndices_.empty()); + JS_ASSERT(cacheList_.empty()); + JS_ASSERT(safepoints_.size() == 0); + JS_ASSERT(graph.mir().numScripts() == 0); + return true; +} + bool CodeGenerator::generate() { @@ -5685,6 +5735,78 @@ CodeGenerator::visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool) return true; } +bool +CodeGenerator::visitAsmJSCall(LAsmJSCall *ins) +{ + MAsmJSCall *mir = ins->mir(); + + if (mir->spIncrement()) + masm.freeStack(mir->spIncrement()); + + JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0); +#ifdef DEBUG + Label ok; + JS_ASSERT(IsPowerOfTwo(StackAlignment)); + masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok); + masm.breakpoint(); + masm.bind(&ok); +#endif + + MAsmJSCall::Callee callee = mir->callee(); + switch (callee.which()) { + case MAsmJSCall::Callee::Internal: + masm.call(callee.internal()); + break; + case MAsmJSCall::Callee::Dynamic: + masm.call(ToRegister(ins->getOperand(mir->dynamicCalleeOperandIndex()))); + break; + case MAsmJSCall::Callee::Builtin: + masm.call(ImmWord(callee.builtin())); + break; + } + + if (mir->spIncrement()) + masm.reserveStack(mir->spIncrement()); + + postAsmJSCall(ins); + return true; +} + +bool +CodeGenerator::visitAsmJSParameter(LAsmJSParameter *lir) +{ + return true; +} + +bool +CodeGenerator::visitAsmJSReturn(LAsmJSReturn *lir) +{ + // Don't emit a jump to the return label if this is the last block. + if (current->mir() != *gen->graph().poBegin()) + masm.jump(returnLabel_); + return true; +} + +bool +CodeGenerator::visitAsmJSVoidReturn(LAsmJSVoidReturn *lir) +{ + // Don't emit a jump to the return label if this is the last block. + if (current->mir() != *gen->graph().poBegin()) + masm.jump(returnLabel_); + return true; +} + +bool +CodeGenerator::visitAsmJSCheckOverRecursed(LAsmJSCheckOverRecursed *lir) +{ + uintptr_t *limitAddr = &gen->compartment->rt->mainThread.nativeStackLimit; + masm.branchPtr(Assembler::AboveOrEqual, + AbsoluteAddress(limitAddr), + StackPointer, + lir->mir()->onError()); + return true; +} + } // namespace ion } // namespace js diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index 0070f119c85..e60d4064875 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -41,10 +41,11 @@ class CodeGenerator : public CodeGeneratorSpecific bool generateBody(); public: - CodeGenerator(MIRGenerator *gen, LIRGraph *graph); + CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm = NULL); public: bool generate(); + bool generateAsmJS(); bool link(); bool visitLabel(LLabel *lir); @@ -135,6 +136,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitAbsI(LAbsI *lir); bool visitPowI(LPowI *lir); bool visitPowD(LPowD *lir); + bool visitNegI(LNegI *lir); bool visitNegD(LNegD *lir); bool visitRandom(LRandom *lir); bool visitMathFunctionD(LMathFunctionD *ins); @@ -209,9 +211,14 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitSetDOMProperty(LSetDOMProperty *lir); bool visitCallDOMNative(LCallDOMNative *lir); bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir); + bool visitAsmJSCall(LAsmJSCall *lir); + bool visitAsmJSParameter(LAsmJSParameter *lir); + bool visitAsmJSReturn(LAsmJSReturn *ret); + bool visitAsmJSVoidReturn(LAsmJSVoidReturn *ret); bool visitCheckOverRecursed(LCheckOverRecursed *lir); bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool); + bool visitAsmJSCheckOverRecursed(LAsmJSCheckOverRecursed *lir); bool visitParCheckOverRecursed(LParCheckOverRecursed *lir); bool visitParCheckOverRecursedFailure(ParCheckOverRecursedFailure *ool); diff --git a/js/src/ion/CompileInfo.h b/js/src/ion/CompileInfo.h index 9e07f931435..5dbc218753b 100644 --- a/js/src/ion/CompileInfo.h +++ b/js/src/ion/CompileInfo.h @@ -8,6 +8,8 @@ #ifndef jsion_compileinfo_h__ #define jsion_compileinfo_h__ +#include "Registers.h" + namespace js { namespace ion { @@ -36,7 +38,21 @@ class CompileInfo executionMode_(executionMode) { JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY); - nslots_ = script->nslots + CountArgSlots(fun); + nimplicit_ = 1 /* scope chain */ + (fun ? 1 /* this */: 0); + nargs_ = fun ? fun->nargs : 0; + nlocals_ = script->nfixed; + nstack_ = script->nslots - script->nfixed; + nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_; + } + + CompileInfo(unsigned nlocals) + : script_(NULL), fun_(NULL), osrPc_(NULL), constructing_(false) + { + nimplicit_ = 0; + nargs_ = 0; + nlocals_ = nlocals; + nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */ + nslots_ = nlocals_ + nstack_; } RawScript script() const { @@ -90,16 +106,17 @@ class CompileInfo } unsigned nargs() const { - return fun()->nargs; + return nargs_; } unsigned nlocals() const { - return script()->nfixed; + return nlocals_; } unsigned ninvoke() const { - return nlocals() + CountArgSlots(fun()); + return nslots_ - nstack_; } uint32_t scopeChainSlot() const { + JS_ASSERT(script()); return 0; } uint32_t thisSlot() const { @@ -107,14 +124,14 @@ class CompileInfo return 1; } uint32_t firstArgSlot() const { - JS_ASSERT(fun()); - return 2; + return nimplicit_; } uint32_t argSlot(uint32_t i) const { - return firstArgSlot() + i; + JS_ASSERT(i < nargs_); + return nimplicit_ + i; } uint32_t firstLocalSlot() const { - return CountArgSlots(fun()); + return nimplicit_ + nargs_; } uint32_t localSlot(uint32_t i) const { return firstLocalSlot() + i; @@ -139,9 +156,13 @@ class CompileInfo } private: + unsigned nimplicit_; + unsigned nargs_; + unsigned nlocals_; + unsigned nstack_; + unsigned nslots_; JSScript *script_; JSFunction *fun_; - unsigned nslots_; jsbytecode *osrPc_; bool constructing_; ExecutionMode executionMode_; diff --git a/js/src/ion/EffectiveAddressAnalysis.cpp b/js/src/ion/EffectiveAddressAnalysis.cpp new file mode 100644 index 00000000000..76fa88a36e0 --- /dev/null +++ b/js/src/ion/EffectiveAddressAnalysis.cpp @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "EffectiveAddressAnalysis.h" + +using namespace js; +using namespace ion; + +#ifdef JS_ASMJS +static void +AnalyzeLsh(MBasicBlock *block, MLsh *lsh) +{ + if (lsh->specialization() != MIRType_Int32) + return; + + MDefinition *index = lsh->lhs(); + JS_ASSERT(index->type() == MIRType_Int32); + + MDefinition *shift = lsh->rhs(); + if (!shift->isConstant()) + return; + + Value shiftValue = shift->toConstant()->value(); + if (!shiftValue.isInt32() || !IsShiftInScaleRange(shiftValue.toInt32())) + return; + + Scale scale = ShiftToScale(shiftValue.toInt32()); + + int32_t displacement = 0; + MInstruction *last = lsh; + MDefinition *base = NULL; + while (true) { + if (last->useCount() != 1) + break; + + MUseIterator use = last->usesBegin(); + if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isAdd()) + break; + + MAdd *add = use->consumer()->toDefinition()->toAdd(); + if (add->specialization() != MIRType_Int32 || !add->isTruncated()) + break; + + MDefinition *other = add->getOperand(1 - use->index()); + + if (other->isConstant()) { + displacement += other->toConstant()->value().toInt32(); + } else { + if (base) + break; + base = other; + } + + last = add; + } + + if (!base) { + uint32_t elemSize = 1 << ScaleToShift(scale); + if (displacement % elemSize != 0) + return; + + if (last->useCount() != 1) + return; + + MUseIterator use = last->usesBegin(); + if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isBitAnd()) + return; + + MBitAnd *bitAnd = use->consumer()->toDefinition()->toBitAnd(); + MDefinition *other = bitAnd->getOperand(1 - use->index()); + if (!other->isConstant() || !other->toConstant()->value().isInt32()) + return; + + uint32_t bitsClearedByShift = elemSize - 1; + uint32_t bitsClearedByMask = ~uint32_t(other->toConstant()->value().toInt32()); + if ((bitsClearedByShift & bitsClearedByMask) != bitsClearedByMask) + return; + + bitAnd->replaceAllUsesWith(last); + return; + } + + MEffectiveAddress *eaddr = MEffectiveAddress::New(base, index, scale, displacement); + last->replaceAllUsesWith(eaddr); + block->insertAfter(last, eaddr); +} +#endif + +// This analysis converts patterns of the form: +// truncate(x + (y << {0,1,2,3})) +// truncate(x + (y << {0,1,2,3}) + imm32) +// into a single lea instruction, and patterns of the form: +// asmload(x + imm32) +// asmload(x << {0,1,2,3}) +// asmload((x << {0,1,2,3}) + imm32) +// asmload((x << {0,1,2,3}) & mask) (where mask is redundant with shift) +// asmload(((x << {0,1,2,3}) + imm32) & mask) (where mask is redundant with shift + imm32) +// into a single asmload instruction (and for asmstore too). +// +// Additionally, we should consider the general forms: +// truncate(x + y + imm32) +// truncate((y << {0,1,2,3}) + imm32) +bool +EffectiveAddressAnalysis::analyze() +{ +#if defined(JS_ASMJS) + for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { + for (MInstructionIterator i = block->begin(); i != block->end(); i++) { + if (i->isLsh()) + AnalyzeLsh(*block, i->toLsh()); + } + } +#endif + return true; +} diff --git a/js/src/ion/EffectiveAddressAnalysis.h b/js/src/ion/EffectiveAddressAnalysis.h new file mode 100644 index 00000000000..e7f3dc9f2e7 --- /dev/null +++ b/js/src/ion/EffectiveAddressAnalysis.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et 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 jsion_effective_address_analysis_h__ +#define jsion_effective_address_analysis_h__ + +#include "MIR.h" +#include "MIRGraph.h" + +namespace js { +namespace ion { + +class EffectiveAddressAnalysis +{ + MIRGraph &graph_; + + public: + EffectiveAddressAnalysis(MIRGraph &graph) + : graph_(graph) + {} + + bool analyze(); +}; + +} /* namespace ion */ +} /* namespace js */ + +#endif diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index 5562a80a932..6f06a0b25b1 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -28,6 +28,7 @@ #include "BacktrackingAllocator.h" #include "StupidAllocator.h" #include "UnreachableCodeElimination.h" +#include "EffectiveAddressAnalysis.h" #if defined(JS_CPU_X86) # include "x86/Lowering-x86.h" @@ -356,7 +357,7 @@ IonCode::copyFrom(MacroAssembler &masm) preBarrierTableBytes_ = masm.preBarrierTableBytes(); masm.copyPreBarrierTable(code_ + preBarrierTableOffset()); - masm.processCodeLabels(this); + masm.processCodeLabels(code_); } void @@ -946,6 +947,17 @@ OptimizeMIR(MIRGenerator *mir) return false; } + if (js_IonOptions.eaa) { + EffectiveAddressAnalysis eaa(graph); + if (!eaa.analyze()) + return false; + IonSpewPass("Effective Address Analysis"); + AssertExtendedGraphCoherency(graph); + + if (mir->shouldCancel("Effective Address Analysis")) + return false; + } + if (!EliminateDeadCode(mir, graph)) return false; IonSpewPass("DCE"); @@ -981,7 +993,7 @@ OptimizeMIR(MIRGenerator *mir) } CodeGenerator * -GenerateLIR(MIRGenerator *mir) +GenerateLIR(MIRGenerator *mir, MacroAssembler *maybeMasm = NULL) { MIRGraph &graph = mir->graph(); @@ -1055,21 +1067,33 @@ GenerateLIR(MIRGenerator *mir) if (mir->shouldCancel("Allocate Registers")) return NULL; - CodeGenerator *codegen = js_new(mir, lir); - if (!codegen || !codegen->generate()) { + CodeGenerator *codegen = js_new(mir, lir, maybeMasm); + if (!codegen) { js_delete(codegen); return NULL; } + if (mir->compilingAsmJS()) { + if (!codegen->generateAsmJS()) { + js_delete(codegen); + return NULL; + } + } else { + if (!codegen->generate()) { + js_delete(codegen); + return NULL; + } + } + return codegen; } CodeGenerator * -CompileBackEnd(MIRGenerator *mir) +CompileBackEnd(MIRGenerator *mir, MacroAssembler *maybeMasm) { if (!OptimizeMIR(mir)) return NULL; - return GenerateLIR(mir); + return GenerateLIR(mir, maybeMasm); } class SequentialCompileContext { diff --git a/js/src/ion/Ion.h b/js/src/ion/Ion.h index e3ec932c452..cc0252afae5 100644 --- a/js/src/ion/Ion.h +++ b/js/src/ion/Ion.h @@ -81,6 +81,11 @@ struct IonOptions // Default: true bool uce; + // Toggles whether Effective Address Analysis is performed. + // + // Default: true + bool eaa; + // Toggles whether compilation occurs off the main thread. // // Default: true iff there are at least two CPUs available @@ -189,6 +194,7 @@ struct IonOptions edgeCaseAnalysis(true), rangeAnalysis(true), uce(true), + eaa(true), parallelCompilation(false), usesBeforeCompile(10240), usesBeforeCompileNoJaeger(40), @@ -310,7 +316,7 @@ class IonBuilder; class MIRGenerator; class CodeGenerator; -CodeGenerator *CompileBackEnd(MIRGenerator *mir); +CodeGenerator *CompileBackEnd(MIRGenerator *mir, MacroAssembler *maybeMasm = NULL); void AttachFinishedCompilations(JSContext *cx); void FinishOffThreadBuilder(IonBuilder *builder); diff --git a/js/src/ion/IonAllocPolicy.h b/js/src/ion/IonAllocPolicy.h index c4e79d56a15..8c828f71528 100644 --- a/js/src/ion/IonAllocPolicy.h +++ b/js/src/ion/IonAllocPolicy.h @@ -161,6 +161,9 @@ class TempObjectPool void free(T *obj) { freed_.pushFront(obj); } + void clear() { + freed_.clear(); + } }; } // namespace ion diff --git a/js/src/ion/IonAnalysis.cpp b/js/src/ion/IonAnalysis.cpp index 7e6cddea03d..fc13e62faec 100644 --- a/js/src/ion/IonAnalysis.cpp +++ b/js/src/ion/IonAnalysis.cpp @@ -1105,6 +1105,7 @@ ion::ExtractLinearInequality(MTest *test, BranchDirection direction, MDefinition *lhs = compare->getOperand(0); MDefinition *rhs = compare->getOperand(1); + // TODO: optimize Compare_UInt32 if (compare->compareType() != MCompare::Compare_Int32) return false; diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index 18d65a297dd..41db6cd609f 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -5672,6 +5672,11 @@ IonBuilder::getTypedArrayElements(MDefinition *obj) if (obj->isConstant() && obj->toConstant()->value().isObject()) { JSObject *array = &obj->toConstant()->value().toObject(); void *data = TypedArray::viewData(array); + + // The 'data' pointer can change in rare circumstances + // (ArrayBufferObject::changeContents). + types::HeapTypeSet::WatchObjectStateChange(cx, array->getType(cx)); + obj->setFoldedUnchecked(); return MConstantElements::New(data); } diff --git a/js/src/ion/IonMacroAssembler.cpp b/js/src/ion/IonMacroAssembler.cpp index eedc68d6722..8e9fb27fde4 100644 --- a/js/src/ion/IonMacroAssembler.cpp +++ b/js/src/ion/IonMacroAssembler.cpp @@ -9,6 +9,7 @@ #include "ion/Bailouts.h" #include "ion/IonMacroAssembler.h" +#include "ion/MIR.h" #include "js/RootingAPI.h" #include "vm/ForkJoin.h" @@ -241,21 +242,12 @@ MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest break; case TypedArray::TYPE_FLOAT32: case TypedArray::TYPE_FLOAT64: - { if (arrayType == js::TypedArray::TYPE_FLOAT32) loadFloatAsDouble(src, dest.fpu()); else loadDouble(src, dest.fpu()); - - // Make sure NaN gets canonicalized. - Label notNaN; - branchDouble(DoubleOrdered, dest.fpu(), dest.fpu(), ¬NaN); - { - loadStaticDouble(&js_NaN, dest.fpu()); - } - bind(¬NaN); + canonicalizeDouble(dest.fpu()); break; - } default: JS_NOT_REACHED("Invalid typed array type"); break; @@ -556,7 +548,7 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register branchTest32(Assembler::Zero, temp, atomBit, ¬Atom); cmpPtr(left, right); - emitSet(JSOpToCondition(op), result); + emitSet(JSOpToCondition(MCompare::Compare_String, op), result); jump(&done); bind(¬Atom); @@ -833,3 +825,24 @@ MacroAssembler::printf(const char *output, Register value) PopRegsInMask(RegisterSet::Volatile()); } + +#ifdef JS_ASMJS +ABIArgIter::ABIArgIter(const MIRTypeVector &types) + : gen_(), + types_(types), + i_(0) +{ + if (!done()) + gen_.next(types_[i_]); +} + +void +ABIArgIter::operator++(int) +{ + JS_ASSERT(!done()); + i_++; + if (!done()) + gen_.next(types_[i_]); +} +#endif + diff --git a/js/src/ion/IonMacroAssembler.h b/js/src/ion/IonMacroAssembler.h index 1bbcb9d3c4d..8d5b3195027 100644 --- a/js/src/ion/IonMacroAssembler.h +++ b/js/src/ion/IonMacroAssembler.h @@ -72,9 +72,9 @@ class MacroAssembler : public MacroAssemblerSpecific // If instrumentation should be emitted, then the sps parameter should be // provided, but otherwise it can be safely omitted to prevent all // instrumentation from being emitted. - MacroAssembler(IonInstrumentation *sps = NULL) + MacroAssembler() : enoughMemory_(true), - sps_(sps) + sps_(NULL) { JSContext *cx = GetIonContext()->cx; if (cx) @@ -92,7 +92,7 @@ class MacroAssembler : public MacroAssemblerSpecific // (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp). MacroAssembler(JSContext *cx) : enoughMemory_(true), - sps_(NULL) // no need for instrumentation in trampolines and such + sps_(NULL) { constructRoot(cx); ionContext_.construct(cx, cx->compartment, (js::ion::TempAllocator *)NULL); @@ -103,6 +103,15 @@ class MacroAssembler : public MacroAssemblerSpecific #endif } + void setInstrumentation(IonInstrumentation *sps) { + sps_ = sps; + } + + void resetForNewCodeGenerator() { + setFramePushed(0); + moveResolver_.clearTempObjectPool(); + } + void constructRoot(JSContext *cx) { autoRooter_.construct(cx, this); } @@ -445,6 +454,13 @@ class MacroAssembler : public MacroAssemblerSpecific bind(&done); } + void canonicalizeDouble(FloatRegister reg) { + Label notNaN; + branchDouble(DoubleOrdered, reg, reg, ¬NaN); + loadStaticDouble(&js_NaN, reg); + bind(¬NaN); + } + template void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail); @@ -545,12 +561,16 @@ class MacroAssembler : public MacroAssemblerSpecific freeStack(IonExitFooterFrame::Size()); } + bool hasEnteredExitFrame() const { + return exitCodePatch_.offset() != 0; + } + void link(IonCode *code) { JS_ASSERT(!oom()); // If this code can transition to C++ code and witness a GC, then we need to store // the IonCode onto the stack in order to GC it correctly. exitCodePatch should // be unset if the code never needed to push its IonCode*. - if (exitCodePatch_.offset() != 0) { + if (hasEnteredExitFrame()) { patchDataWithValueCheck(CodeLocationLabel(code, exitCodePatch_), ImmWord(uintptr_t(code)), ImmWord(uintptr_t(-1))); @@ -706,30 +726,6 @@ class MacroAssembler : public MacroAssemblerSpecific void printf(const char *output, Register value); }; -static inline Assembler::Condition -JSOpToCondition(JSOp op) -{ - switch (op) { - case JSOP_EQ: - case JSOP_STRICTEQ: - return Assembler::Equal; - case JSOP_NE: - case JSOP_STRICTNE: - return Assembler::NotEqual; - case JSOP_LT: - return Assembler::LessThan; - case JSOP_LE: - return Assembler::LessThanOrEqual; - case JSOP_GT: - return Assembler::GreaterThan; - case JSOP_GE: - return Assembler::GreaterThanOrEqual; - default: - JS_NOT_REACHED("Unrecognized comparison operation"); - return Assembler::Equal; - } -} - static inline Assembler::DoubleCondition JSOpToDoubleCondition(JSOp op) { @@ -754,6 +750,30 @@ JSOpToDoubleCondition(JSOp op) } } +typedef Vector MIRTypeVector; + +#ifdef JS_ASMJS +class ABIArgIter +{ + ABIArgGenerator gen_; + const MIRTypeVector &types_; + unsigned i_; + + public: + ABIArgIter(const MIRTypeVector &argTypes); + + void operator++(int); + bool done() const { return i_ == types_.length(); } + + ABIArg *operator->() { JS_ASSERT(!done()); return &gen_.current(); } + ABIArg &operator*() { JS_ASSERT(!done()); return gen_.current(); } + + unsigned index() const { JS_ASSERT(!done()); return i_; } + MIRType mirType() const { JS_ASSERT(!done()); return types_[i_]; } + uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); } +}; +#endif + } // namespace ion } // namespace js diff --git a/js/src/ion/IonTypes.h b/js/src/ion/IonTypes.h index ee3870cd292..aab161adcf5 100644 --- a/js/src/ion/IonTypes.h +++ b/js/src/ion/IonTypes.h @@ -67,7 +67,7 @@ enum MIRType MIRType_None, // Invalid, used as a placeholder. MIRType_Slots, // A slots vector MIRType_Elements, // An elements vector - MIRType_StackFrame, // StackFrame pointer for OSR. + MIRType_Pointer, // An opaque pointer that receives no special treatment MIRType_Shape, // A Shape pointer. MIRType_ForkJoinSlice // js::ForkJoinSlice* }; diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index 56bfaffc62b..e85f3642a35 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -508,8 +508,7 @@ class LCheckOverRecursed : public LInstructionHelper<0, 0, 1> public: LIR_HEADER(CheckOverRecursed) - LCheckOverRecursed(const LDefinition &limitreg) - { + LCheckOverRecursed(const LDefinition &limitreg) { setTemp(0, limitreg); } @@ -1884,6 +1883,16 @@ class LMinMaxD : public LInstructionHelper<1, 2, 0> } }; +// Negative of an integer +class LNegI : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(NegI); + LNegI(const LAllocation &num) { + setOperand(0, num); + } +}; + // Negative of a double. class LNegD : public LInstructionHelper<1, 1, 0> { @@ -3047,6 +3056,26 @@ class LStoreTypedArrayElement : public LInstructionHelper<0, 3, 0> } }; +class LEffectiveAddress : public LInstructionHelper<1, 2, 0> +{ + public: + LIR_HEADER(EffectiveAddress); + + LEffectiveAddress(const LAllocation &base, const LAllocation &index) { + setOperand(0, base); + setOperand(1, index); + } + const MEffectiveAddress *mir() const { + return mir_->toEffectiveAddress(); + } + const LAllocation *base() { + return getOperand(0); + } + const LAllocation *index() { + return getOperand(1); + } +}; + class LClampIToUint8 : public LInstructionHelper<1, 1, 0> { public: @@ -3953,6 +3982,173 @@ class LFunctionBoundary : public LInstructionHelper<0, 0, 1> } }; +class LAsmJSLoadHeap : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(AsmJSLoadHeap); + LAsmJSLoadHeap(const LAllocation &ptr) { + setOperand(0, ptr); + } + MAsmJSLoadHeap *mir() const { + return mir_->toAsmJSLoadHeap(); + } + const LAllocation *ptr() { + return getOperand(0); + } +}; + +class LAsmJSStoreHeap : public LInstructionHelper<0, 2, 0> +{ + public: + LIR_HEADER(AsmJSStoreHeap); + LAsmJSStoreHeap(const LAllocation &ptr, const LAllocation &value) { + setOperand(0, ptr); + setOperand(1, value); + } + MAsmJSStoreHeap *mir() const { + return mir_->toAsmJSStoreHeap(); + } + const LAllocation *ptr() { + return getOperand(0); + } + const LAllocation *value() { + return getOperand(1); + } +}; + +class LAsmJSLoadGlobalVar : public LInstructionHelper<1, 0, 0> +{ + public: + LIR_HEADER(AsmJSLoadGlobalVar); + MAsmJSLoadGlobalVar *mir() const { + return mir_->toAsmJSLoadGlobalVar(); + } +}; + +class LAsmJSStoreGlobalVar : public LInstructionHelper<0, 1, 0> +{ + public: + LIR_HEADER(AsmJSStoreGlobalVar); + LAsmJSStoreGlobalVar(const LAllocation &value) { + setOperand(0, value); + } + MAsmJSStoreGlobalVar *mir() const { + return mir_->toAsmJSStoreGlobalVar(); + } + const LAllocation *value() { + return getOperand(0); + } +}; + +class LAsmJSLoadFFIFunc : public LInstructionHelper<1, 0, 0> +{ + public: + LIR_HEADER(AsmJSLoadFFIFunc); + MAsmJSLoadFFIFunc *mir() const { + return mir_->toAsmJSLoadFFIFunc(); + } +}; + +class LAsmJSParameter : public LInstructionHelper<1, 0, 0> +{ + public: + LIR_HEADER(AsmJSParameter); +}; + +class LAsmJSReturn : public LInstructionHelper<0, 1, 0> +{ + public: + LIR_HEADER(AsmJSReturn); +}; + +class LAsmJSVoidReturn : public LInstructionHelper<0, 0, 0> +{ + public: + LIR_HEADER(AsmJSVoidReturn); +}; + +class LAsmJSPassStackArg : public LInstructionHelper<0, 1, 0> +{ + public: + LIR_HEADER(AsmJSPassStackArg); + LAsmJSPassStackArg(const LAllocation &arg) { + setOperand(0, arg); + } + MAsmJSPassStackArg *mir() const { + return mirRaw()->toAsmJSPassStackArg(); + } + const LAllocation *arg() { + return getOperand(0); + } +}; + +class LAsmJSCall : public LInstruction +{ + LAllocation *operands_; + uint32_t numOperands_; + LDefinition def_; + + public: + LIR_HEADER(AsmJSCall); + + LAsmJSCall(LAllocation *operands, uint32_t numOperands) + : operands_(operands), + numOperands_(numOperands), + def_(LDefinition::BogusTemp()) + {} + + MAsmJSCall *mir() const { + return mir_->toAsmJSCall(); + } + + bool isCall() const { + return true; + }; + + // LInstruction interface + size_t numDefs() const { + return def_.isBogusTemp() ? 0 : 1; + } + LDefinition *getDef(size_t index) { + JS_ASSERT(numDefs() == 1); + JS_ASSERT(index == 0); + return &def_; + } + void setDef(size_t index, const LDefinition &def) { + JS_ASSERT(index == 0); + def_ = def; + } + size_t numOperands() const { + return numOperands_; + } + LAllocation *getOperand(size_t index) { + JS_ASSERT(index < numOperands_); + return &operands_[index]; + } + void setOperand(size_t index, const LAllocation &a) { + JS_ASSERT(index < numOperands_); + operands_[index] = a; + } + size_t numTemps() const { + return 0; + } + LDefinition *getTemp(size_t index) { + JS_NOT_REACHED("no temps"); + } + void setTemp(size_t index, const LDefinition &a) { + JS_NOT_REACHED("no temps"); + } +}; + +class LAsmJSCheckOverRecursed : public LInstructionHelper<0, 0, 0> +{ + public: + LIR_HEADER(AsmJSCheckOverRecursed) + + MAsmJSCheckOverRecursed *mir() const { + return mir_->toAsmJSCheckOverRecursed(); + } +}; } // namespace ion } // namespace js diff --git a/js/src/ion/LIR.cpp b/js/src/ion/LIR.cpp index 998157cd84b..9eb1bf63850 100644 --- a/js/src/ion/LIR.cpp +++ b/js/src/ion/LIR.cpp @@ -254,7 +254,10 @@ LAllocation::toString() const case LAllocation::DOUBLE_SLOT: JS_snprintf(buf, sizeof(buf), "stack:d%d", toStackSlot()->slot()); return buf; - case LAllocation::ARGUMENT: + case LAllocation::INT_ARGUMENT: + JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index()); + return buf; + case LAllocation::DOUBLE_ARGUMENT: JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index()); return buf; case LAllocation::USE: diff --git a/js/src/ion/LIR.h b/js/src/ion/LIR.h index c0018ebae25..01435f2c40f 100644 --- a/js/src/ion/LIR.h +++ b/js/src/ion/LIR.h @@ -39,7 +39,6 @@ class MTableSwitch; class MIRGenerator; class MSnapshot; -static const uint32_t MAX_VIRTUAL_REGISTERS = (1 << 21) - 1; static const uint32_t VREG_INCREMENT = 1; static const uint32_t THIS_FRAME_SLOT = 0; @@ -66,7 +65,7 @@ class LAllocation : public TempObject static const uintptr_t TAG_BIT = 1; static const uintptr_t TAG_SHIFT = 0; static const uintptr_t TAG_MASK = 1 << TAG_SHIFT; - static const uintptr_t KIND_BITS = 3; + static const uintptr_t KIND_BITS = 4; static const uintptr_t KIND_SHIFT = TAG_SHIFT + TAG_BIT; static const uintptr_t KIND_MASK = (1 << KIND_BITS) - 1; static const uintptr_t DATA_BITS = (sizeof(uint32_t) * 8) - KIND_BITS - TAG_BIT; @@ -82,7 +81,8 @@ class LAllocation : public TempObject FPU, // Floating-point register. STACK_SLOT, // 32-bit stack slot. DOUBLE_SLOT, // 64-bit stack slot. - ARGUMENT // Argument slot. + INT_ARGUMENT, // Argument slot that gets loaded into a GPR. + DOUBLE_ARGUMENT // Argument slot to be loaded into an FPR }; protected: @@ -161,7 +161,7 @@ class LAllocation : public TempObject return kind() == STACK_SLOT || kind() == DOUBLE_SLOT; } bool isArgument() const { - return kind() == ARGUMENT; + return kind() == INT_ARGUMENT || kind() == DOUBLE_ARGUMENT; } bool isRegister() const { return isGeneralReg() || isFloatReg(); @@ -170,7 +170,7 @@ class LAllocation : public TempObject return isStackSlot() || isArgument(); } bool isDouble() const { - return kind() == DOUBLE_SLOT || kind() == FPU; + return kind() == DOUBLE_SLOT || kind() == FPU || kind() == DOUBLE_ARGUMENT; } inline LUse *toUse(); inline const LUse *toUse() const; @@ -219,12 +219,12 @@ class LUse : public LAllocation static const uint32_t USED_AT_START_SHIFT = REG_SHIFT + REG_BITS; static const uint32_t USED_AT_START_MASK = (1 << USED_AT_START_BITS) - 1; + public: // Virtual registers get the remaining 20 bits. static const uint32_t VREG_BITS = DATA_BITS - (USED_AT_START_SHIFT + USED_AT_START_BITS); static const uint32_t VREG_SHIFT = USED_AT_START_SHIFT + USED_AT_START_BITS; static const uint32_t VREG_MASK = (1 << VREG_BITS) - 1; - public: enum Policy { // Input should be in a read-only register or stack slot. ANY, @@ -278,7 +278,6 @@ class LUse : public LAllocation } void setVirtualRegister(uint32_t index) { - JS_STATIC_ASSERT(VREG_MASK <= MAX_VIRTUAL_REGISTERS); JS_ASSERT(index < VREG_MASK); uint32_t old = data() & ~(VREG_MASK << VREG_SHIFT); @@ -305,6 +304,8 @@ class LUse : public LAllocation } }; +static const uint32_t MAX_VIRTUAL_REGISTERS = LUse::VREG_MASK; + class LGeneralReg : public LAllocation { public: @@ -375,9 +376,11 @@ class LStackSlot : public LAllocation class LArgument : public LAllocation { public: - explicit LArgument(int32_t index) - : LAllocation(ARGUMENT, index) - { } + explicit LArgument(LAllocation::Kind kind, int32_t index) + : LAllocation(kind, index) + { + JS_ASSERT(kind == INT_ARGUMENT || kind == DOUBLE_ARGUMENT); + } int32_t index() const { return data(); @@ -544,7 +547,7 @@ class LDefinition // When we begin allocating slots vectors from the GC, this will // need to change to ::OBJECT. return LDefinition::GENERAL; - case MIRType_StackFrame: + case MIRType_Pointer: return LDefinition::GENERAL; case MIRType_ForkJoinSlice: return LDefinition::GENERAL; @@ -694,10 +697,8 @@ class LInstructionVisitor public: void setInstruction(LInstruction *ins) { ins_ = ins; - if (ins->mirRaw()) { + if (ins->mirRaw()) lastPC_ = ins->mirRaw()->trackedPc(); - JS_ASSERT(lastPC_ != NULL); - } } LInstructionVisitor() diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index 38d67821d5c..895eafab4fc 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -84,6 +84,7 @@ _(EmulatesUndefinedAndBranch) \ _(MinMaxI) \ _(MinMaxD) \ + _(NegI) \ _(NegD) \ _(AbsI) \ _(AbsD) \ @@ -154,6 +155,7 @@ _(LoadTypedArrayElement) \ _(LoadTypedArrayElementHole) \ _(StoreTypedArrayElement) \ + _(EffectiveAddress) \ _(ClampIToUint8) \ _(ClampDToUint8) \ _(ClampVToUint8) \ @@ -199,11 +201,22 @@ _(InstanceOfV) \ _(CallInstanceOf) \ _(InterruptCheck) \ - _(ParCheckInterrupt) \ _(FunctionBoundary) \ _(GetDOMProperty) \ _(SetDOMProperty) \ - _(CallDOMNative) + _(CallDOMNative) \ + _(AsmJSLoadHeap) \ + _(AsmJSStoreHeap) \ + _(AsmJSLoadGlobalVar) \ + _(AsmJSStoreGlobalVar) \ + _(AsmJSLoadFFIFunc) \ + _(AsmJSParameter) \ + _(AsmJSReturn) \ + _(AsmJSVoidReturn) \ + _(AsmJSPassStackArg) \ + _(AsmJSCall) \ + _(AsmJSCheckOverRecursed) \ + _(ParCheckInterrupt) #if defined(JS_CPU_X86) # include "x86/LOpcodes-x86.h" diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index 62553079cc1..daa21ca1d71 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -37,14 +37,14 @@ LIRGenerator::visitParameter(MParameter *param) offset *= sizeof(Value); #if defined(JS_NUNBOX32) # if defined(IS_BIG_ENDIAN) - ins->getDef(0)->setOutput(LArgument(offset)); - ins->getDef(1)->setOutput(LArgument(offset + 4)); + ins->getDef(0)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset)); + ins->getDef(1)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset + 4)); # else - ins->getDef(0)->setOutput(LArgument(offset + 4)); - ins->getDef(1)->setOutput(LArgument(offset)); + ins->getDef(0)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset + 4)); + ins->getDef(1)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset)); # endif #elif defined(JS_PUNBOX64) - ins->getDef(0)->setOutput(LArgument(offset)); + ins->getDef(0)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset)); #endif return true; @@ -602,15 +602,19 @@ LIRGenerator::visitTest(MTest *test) // Compare and branch Int32 or Object pointers. if (comp->compareType() == MCompare::Compare_Int32 || + comp->compareType() == MCompare::Compare_UInt32 || comp->compareType() == MCompare::Compare_Object) { JSOp op = ReorderComparison(comp->jsop(), &left, &right); LAllocation lhs = useRegister(left); LAllocation rhs; - if (comp->compareType() == MCompare::Compare_Int32) + if (comp->compareType() == MCompare::Compare_Int32 || + comp->compareType() == MCompare::Compare_UInt32) + { rhs = useAnyOrConstant(right); - else + } else { rhs = useRegister(right); + } LCompareAndBranch *lir = new LCompareAndBranch(op, lhs, rhs, ifTrue, ifFalse); return add(lir, comp); } @@ -775,15 +779,19 @@ LIRGenerator::visitCompare(MCompare *comp) // Compare Int32 or Object pointers. if (comp->compareType() == MCompare::Compare_Int32 || + comp->compareType() == MCompare::Compare_UInt32 || comp->compareType() == MCompare::Compare_Object) { JSOp op = ReorderComparison(comp->jsop(), &left, &right); LAllocation lhs = useRegister(left); LAllocation rhs; - if (comp->compareType() == MCompare::Compare_Int32) + if (comp->compareType() == MCompare::Compare_Int32 || + comp->compareType() == MCompare::Compare_UInt32) + { rhs = useAnyOrConstant(right); - else + } else { rhs = useRegister(right); + } return define(new LCompare(op, lhs, rhs), comp); } @@ -1913,6 +1921,12 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole *ins) return add(lir, ins) && assignSafepoint(lir, ins); } +bool +LIRGenerator::visitEffectiveAddress(MEffectiveAddress *ins) +{ + return define(new LEffectiveAddress(useRegister(ins->base()), useRegister(ins->index())), ins); +} + bool LIRGenerator::visitArrayPopShift(MArrayPopShift *ins) { @@ -2387,6 +2401,105 @@ LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins) assignSafepoint(lir, ins); } +bool +LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) +{ + LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(useRegisterAtStart(ins->ptr())); + return define(lir, ins); +} + +bool +LIRGenerator::visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins) +{ + return define(new LAsmJSLoadGlobalVar, ins); +} + +bool +LIRGenerator::visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins) +{ + return add(new LAsmJSStoreGlobalVar(useRegisterAtStart(ins->value())), ins); +} + +bool +LIRGenerator::visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins) +{ + return define(new LAsmJSLoadFFIFunc, ins); +} + +bool +LIRGenerator::visitAsmJSParameter(MAsmJSParameter *ins) +{ + ABIArg abi = ins->abi(); + if (abi.argInRegister()) + return defineFixed(new LAsmJSParameter, ins, LAllocation(abi.reg())); + + JS_ASSERT(ins->type() == MIRType_Int32 || ins->type() == MIRType_Double); + LAllocation::Kind argKind = ins->type() == MIRType_Int32 + ? LAllocation::INT_ARGUMENT + : LAllocation::DOUBLE_ARGUMENT; + return defineFixed(new LAsmJSParameter, ins, LArgument(argKind, abi.offsetFromArgBase())); +} + +bool +LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins) +{ + MDefinition *rval = ins->getOperand(0); + LAsmJSReturn *lir = new LAsmJSReturn; + if (rval->type() == MIRType_Double) + lir->setOperand(0, useFixed(rval, ReturnFloatReg)); + else if (rval->type() == MIRType_Int32) + lir->setOperand(0, useFixed(rval, ReturnReg)); + else + JS_NOT_REACHED("Unexpected asm.js return type"); + return add(lir); +} + +bool +LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn *ins) +{ + return add(new LAsmJSVoidReturn); +} + +bool +LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg *ins) +{ + if (ins->arg()->type() == MIRType_Double) { + JS_ASSERT(!ins->arg()->isEmittedAtUses()); + return add(new LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins); + } + + return add(new LAsmJSPassStackArg(useRegisterOrConstantAtStart(ins->arg())), ins); +} + +bool +LIRGenerator::visitAsmJSCall(MAsmJSCall *ins) +{ + gen->setPerformsAsmJSCall(); + + LAllocation *args = gen->allocate(ins->numOperands()); + if (!args) + return false; + + for (unsigned i = 0; i < ins->numArgs(); i++) + args[i] = useFixed(ins->getOperand(i), ins->registerForArg(i)); + + if (ins->callee().which() == MAsmJSCall::Callee::Dynamic) + args[ins->dynamicCalleeOperandIndex()] = useFixed(ins->callee().dynamic(), CallTempReg0); + + LInstruction *lir = new LAsmJSCall(args, ins->numOperands()); + if (ins->type() == MIRType_None) { + lir->setMir(ins); + return add(lir); + } + return defineReturn(lir, ins); +} + +bool +LIRGenerator::visitAsmJSCheckOverRecursed(MAsmJSCheckOverRecursed *ins) +{ + return add(new LAsmJSCheckOverRecursed(), ins); +} + bool LIRGenerator::visitSetDOMProperty(MSetDOMProperty *ins) { @@ -2431,7 +2544,6 @@ LIRGenerator::visitGetDOMProperty(MGetDOMProperty *ins) return defineReturn(lir, ins) && assignSafepoint(lir, ins); } - static void SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint) { diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index ebbc45d4829..e8cf83e49b3 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -178,6 +178,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitLoadElementHole(MLoadElementHole *ins); bool visitStoreElement(MStoreElement *ins); bool visitStoreElementHole(MStoreElementHole *ins); + bool visitEffectiveAddress(MEffectiveAddress *ins); bool visitArrayPopShift(MArrayPopShift *ins); bool visitArrayPush(MArrayPush *ins); bool visitArrayConcat(MArrayConcat *ins); @@ -214,6 +215,16 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitInstanceOf(MInstanceOf *ins); bool visitCallInstanceOf(MCallInstanceOf *ins); bool visitFunctionBoundary(MFunctionBoundary *ins); + bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); + bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins); + bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins); + bool visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins); + bool visitAsmJSParameter(MAsmJSParameter *ins); + bool visitAsmJSReturn(MAsmJSReturn *ins); + bool visitAsmJSVoidReturn(MAsmJSVoidReturn *ins); + bool visitAsmJSPassStackArg(MAsmJSPassStackArg *ins); + bool visitAsmJSCall(MAsmJSCall *ins); + bool visitAsmJSCheckOverRecursed(MAsmJSCheckOverRecursed *ins); bool visitSetDOMProperty(MSetDOMProperty *ins); bool visitGetDOMProperty(MGetDOMProperty *ins); }; diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index 8324a0886fb..03c761c6b7a 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -472,6 +472,18 @@ MCompare::New(MDefinition *left, MDefinition *right, JSOp op) return new MCompare(left, right, op); } +MCompare * +MCompare::NewAsmJS(MDefinition *left, MDefinition *right, JSOp op, CompareType compareType) +{ + JS_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 || + compareType == Compare_Double); + MCompare *comp = new MCompare(left, right, op); + comp->compareType_ = compareType; + comp->operandMightEmulateUndefined_ = false; + comp->setResultType(MIRType_Int32); + return comp; +} + MTableSwitch * MTableSwitch::New(MDefinition *ins, int32_t low, int32_t high) { @@ -708,6 +720,14 @@ MBinaryBitwiseInstruction::infer(const TypeOracle::BinaryTypes &b) } } +void +MBinaryBitwiseInstruction::specializeForAsmJS() +{ + specialization_ = MIRType_Int32; + JS_ASSERT(type() == MIRType_Int32); + setCommutative(); +} + void MShiftInstruction::infer(const TypeOracle::BinaryTypes &b) { @@ -872,7 +892,7 @@ MBinaryArithInstruction::foldsTo(bool useValueNumbers) bool MAbs::fallible() const { - return !range() || !range()->isInt32(); + return !implicitTruncate_ && (!range() || !range()->isInt32()); } MDefinition * @@ -1193,6 +1213,7 @@ MCompare::inputType() return MIRType_Null; case Compare_Boolean: return MIRType_Boolean; + case Compare_UInt32: case Compare_Int32: return MIRType_Int32; case Compare_Double: @@ -1329,6 +1350,15 @@ MBitNot::New(MDefinition *input) return new MBitNot(input); } +MBitNot * +MBitNot::NewAsmJS(MDefinition *input) +{ + MBitNot *ins = new MBitNot(input); + ins->specialization_ = MIRType_Int32; + JS_ASSERT(ins->type() == MIRType_Int32); + return ins; +} + MDefinition * MBitNot::foldsTo(bool useValueNumbers) { @@ -1390,36 +1420,85 @@ MBitAnd::New(MDefinition *left, MDefinition *right) return new MBitAnd(left, right); } +MBitAnd * +MBitAnd::NewAsmJS(MDefinition *left, MDefinition *right) +{ + MBitAnd *ins = new MBitAnd(left, right); + ins->specializeForAsmJS(); + return ins; +} + MBitOr * MBitOr::New(MDefinition *left, MDefinition *right) { return new MBitOr(left, right); } +MBitOr * +MBitOr::NewAsmJS(MDefinition *left, MDefinition *right) +{ + MBitOr *ins = new MBitOr(left, right); + ins->specializeForAsmJS(); + return ins; +} + MBitXor * MBitXor::New(MDefinition *left, MDefinition *right) { return new MBitXor(left, right); } +MBitXor * +MBitXor::NewAsmJS(MDefinition *left, MDefinition *right) +{ + MBitXor *ins = new MBitXor(left, right); + ins->specializeForAsmJS(); + return ins; +} + MLsh * MLsh::New(MDefinition *left, MDefinition *right) { return new MLsh(left, right); } +MLsh * +MLsh::NewAsmJS(MDefinition *left, MDefinition *right) +{ + MLsh *ins = new MLsh(left, right); + ins->specializeForAsmJS(); + return ins; +} + MRsh * MRsh::New(MDefinition *left, MDefinition *right) { return new MRsh(left, right); } +MRsh * +MRsh::NewAsmJS(MDefinition *left, MDefinition *right) +{ + MRsh *ins = new MRsh(left, right); + ins->specializeForAsmJS(); + return ins; +} + MUrsh * MUrsh::New(MDefinition *left, MDefinition *right) { return new MUrsh(left, right); } +MUrsh * +MUrsh::NewAsmJS(MDefinition *left, MDefinition *right) +{ + MUrsh *ins = new MUrsh(left, right); + ins->specializeForAsmJS(); + ins->canOverflow_ = false; + return ins; +} + MResumePoint * MResumePoint::New(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode) { @@ -1619,7 +1698,7 @@ MCompare::tryFold(bool *result) bool MCompare::evaluateConstantOperands(bool *result) { - if (type() != MIRType_Boolean) + if (type() != MIRType_Boolean && type() != MIRType_Int32) return false; MDefinition *left = getOperand(0); @@ -1668,6 +1747,37 @@ MCompare::evaluateConstantOperands(bool *result) return true; } + if (compareType_ == Compare_UInt32) { + uint32_t lhsUint = uint32_t(lhs.toInt32()); + uint32_t rhsUint = uint32_t(rhs.toInt32()); + + switch (jsop_) { + case JSOP_LT: + *result = (lhsUint < rhsUint); + break; + case JSOP_LE: + *result = (lhsUint <= rhsUint); + break; + case JSOP_GT: + *result = (lhsUint > rhsUint); + break; + case JSOP_GE: + *result = (lhsUint >= rhsUint); + break; + case JSOP_EQ: + *result = (lhsUint == rhsUint); + break; + case JSOP_NE: + *result = (lhsUint != rhsUint); + break; + default: + JS_NOT_REACHED("Unexpected op."); + return false; + } + + return true; + } + if (!lhs.isNumber() || !rhs.isNumber()) return false; @@ -1702,10 +1812,13 @@ MCompare::foldsTo(bool useValueNumbers) { bool result; - if (tryFold(&result)) - return MConstant::New(BooleanValue(result)); - else if (evaluateConstantOperands(&result)) + if (tryFold(&result) || evaluateConstantOperands(&result)) { + if (type() == MIRType_Int32) + return MConstant::New(Int32Value(result)); + + JS_ASSERT(type() == MIRType_Boolean); return MConstant::New(BooleanValue(result)); + } return this; } @@ -1727,7 +1840,10 @@ MNot::foldsTo(bool useValueNumbers) { // Fold if the input is constant if (operand()->isConstant()) { - const Value &v = operand()->toConstant()->value(); + const Value &v = operand()->toConstant()->value(); + if (type() == MIRType_Int32) + return MConstant::New(Int32Value(!ToBoolean(v))); + // ToBoolean can cause no side effects, so this is safe. return MConstant::New(BooleanValue(!ToBoolean(v))); } @@ -1845,3 +1961,43 @@ InlinePropertyTable::trimToAndMaybePatchTargets(AutoObjectVector &targets, IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets", (int)numEntries(), (int)targets.length()); } + +MDefinition * +MAsmJSUnsignedToDouble::foldsTo(bool useValueNumbers) +{ + if (input()->isConstant()) { + const Value &v = input()->toConstant()->value(); + if (v.isInt32()) + return MConstant::New(DoubleValue(uint32_t(v.toInt32()))); + } + + return this; +} + +MAsmJSCall * +MAsmJSCall::New(Callee callee, const Args &args, MIRType resultType, size_t spIncrement) +{ + MAsmJSCall *call = new MAsmJSCall; + call->spIncrement_ = spIncrement; + call->callee_ = callee; + call->setResultType(resultType); + + call->numArgs_ = args.length(); + call->argRegs_ = (AnyRegister *)GetIonContext()->temp->allocate(call->numArgs_ * sizeof(AnyRegister)); + if (!call->argRegs_) + return NULL; + for (size_t i = 0; i < call->numArgs_; i++) + call->argRegs_[i] = args[i].reg; + + call->numOperands_ = call->numArgs_ + (callee.which() == Callee::Dynamic ? 1 : 0); + call->operands_ = (MUse *)GetIonContext()->temp->allocate(call->numOperands_ * sizeof(MUse)); + if (!call->operands_) + return NULL; + for (size_t i = 0; i < call->numArgs_; i++) + call->setOperand(i, args[i].def); + if (callee.which() == Callee::Dynamic) + call->setOperand(call->numArgs_, callee.dynamic()); + + return call; +} + diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index d95b54bccc2..92d98ef8e7d 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -653,7 +653,7 @@ class MOsrEntry : public MNullaryInstruction { protected: MOsrEntry() { - setResultType(MIRType_StackFrame); + setResultType(MIRType_Pointer); } public: @@ -799,7 +799,8 @@ class MTableSwitch int32_t low_; int32_t high_; - MTableSwitch(MDefinition *ins, int32_t low, int32_t high) + MTableSwitch(MDefinition *ins, + int32_t low, int32_t high) : successors_(), blocks_(), low_(low), @@ -822,8 +823,7 @@ class MTableSwitch public: INSTRUCTION_HEADER(TableSwitch) - static MTableSwitch *New(MDefinition *ins, - int32_t low, int32_t high); + static MTableSwitch *New(MDefinition *ins, int32_t low, int32_t high); size_t numSuccessors() const { return successors_.length(); @@ -1656,6 +1656,9 @@ class MCompare // Boolean compared to Boolean Compare_Int32, + // Int32 compared as unsigneds + Compare_UInt32, + // Double compared to Double Compare_Double, @@ -1698,8 +1701,8 @@ class MCompare public: INSTRUCTION_HEADER(Compare) - static MCompare *New(MDefinition *left, MDefinition *right, JSOp op); + static MCompare *NewAsmJS(MDefinition *left, MDefinition *right, JSOp op, CompareType compareType); bool tryFold(bool *result); bool evaluateConstantOperands(bool *result); @@ -1740,7 +1743,8 @@ class MCompare bool congruentTo(MDefinition *const &ins) const { if (!MBinaryInstruction::congruentTo(ins)) return false; - return jsop() == ins->toCompare()->jsop(); + return compareType() == ins->toCompare()->compareType() && + jsop() == ins->toCompare()->jsop(); } }; @@ -1772,6 +1776,55 @@ class MBox : public MUnaryInstruction } }; +// Note: the op may have been inverted during lowering (to put constants in a +// position where they can be immediates), so it is important to use the +// lir->jsop() instead of the mir->jsop() when it is present. +static inline Assembler::Condition +JSOpToCondition(MCompare::CompareType compareType, JSOp op) +{ + if (compareType == MCompare::Compare_UInt32) { + switch (op) { + case JSOP_EQ: + case JSOP_STRICTEQ: + return Assembler::Equal; + case JSOP_NE: + case JSOP_STRICTNE: + return Assembler::NotEqual; + case JSOP_LT: + return Assembler::Below; + case JSOP_LE: + return Assembler::BelowOrEqual; + case JSOP_GT: + return Assembler::Above; + case JSOP_GE: + return Assembler::AboveOrEqual; + default: + JS_NOT_REACHED("Unrecognized comparison operation"); + return Assembler::Equal; + } + } else { + switch (op) { + case JSOP_EQ: + case JSOP_STRICTEQ: + return Assembler::Equal; + case JSOP_NE: + case JSOP_STRICTNE: + return Assembler::NotEqual; + case JSOP_LT: + return Assembler::LessThan; + case JSOP_LE: + return Assembler::LessThanOrEqual; + case JSOP_GT: + return Assembler::GreaterThan; + case JSOP_GE: + return Assembler::GreaterThanOrEqual; + default: + JS_NOT_REACHED("Unrecognized comparison operation"); + return Assembler::Equal; + } + } +} + // Takes a typed value and checks if it is a certain type. If so, the payload // is unpacked and returned as that type. Otherwise, it is considered a // deoptimization. @@ -2089,8 +2142,10 @@ class MToDouble public: INSTRUCTION_HEADER(ToDouble) - static MToDouble *New(MDefinition *def) - { + static MToDouble *New(MDefinition *def) { + return new MToDouble(def); + } + static MToDouble *NewAsmJS(MDefinition *def) { return new MToDouble(def); } @@ -2114,6 +2169,35 @@ class MToDouble bool isOperandTruncated(size_t index) const; }; +// Converts a uint32 to a double (coming from asm.js). +class MAsmJSUnsignedToDouble + : public MUnaryInstruction +{ + MAsmJSUnsignedToDouble(MDefinition *def) + : MUnaryInstruction(def) + { + setResultType(MIRType_Double); + setMovable(); + } + + public: + INSTRUCTION_HEADER(AsmJSUnsignedToDouble); + static MAsmJSUnsignedToDouble *NewAsmJS(MDefinition *def) { + return new MAsmJSUnsignedToDouble(def); + } + + MDefinition *foldsTo(bool useValueNumbers); + MDefinition *input() const { + return getOperand(0); + } + bool congruentTo(MDefinition *const &ins) const { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const { + return AliasSet::None(); + } +}; + // Converts a primitive (either typed or untyped) to an int32. If the input is // not primitive at runtime, a bailout occurs. If the input cannot be converted // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs. @@ -2175,8 +2259,10 @@ class MTruncateToInt32 : public MUnaryInstruction public: INSTRUCTION_HEADER(TruncateToInt32) - static MTruncateToInt32 *New(MDefinition *def) - { + static MTruncateToInt32 *New(MDefinition *def) { + return new MTruncateToInt32(def); + } + static MTruncateToInt32 *NewAsmJS(MDefinition *def) { return new MTruncateToInt32(def); } @@ -2244,6 +2330,7 @@ class MBitNot public: INSTRUCTION_HEADER(BitNot) static MBitNot *New(MDefinition *input); + static MBitNot *NewAsmJS(MDefinition *input); TypePolicy *typePolicy() { return this; @@ -2336,6 +2423,8 @@ class MBinaryBitwiseInstruction setMovable(); } + void specializeForAsmJS(); + public: TypePolicy *typePolicy() { return this; @@ -2368,6 +2457,7 @@ class MBitAnd : public MBinaryBitwiseInstruction public: INSTRUCTION_HEADER(BitAnd) static MBitAnd *New(MDefinition *left, MDefinition *right); + static MBitAnd *NewAsmJS(MDefinition *left, MDefinition *right); MDefinition *foldIfZero(size_t operand) { return getOperand(operand); // 0 & x => 0; @@ -2390,6 +2480,7 @@ class MBitOr : public MBinaryBitwiseInstruction public: INSTRUCTION_HEADER(BitOr) static MBitOr *New(MDefinition *left, MDefinition *right); + static MBitOr *NewAsmJS(MDefinition *left, MDefinition *right); MDefinition *foldIfZero(size_t operand) { return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th @@ -2411,6 +2502,7 @@ class MBitXor : public MBinaryBitwiseInstruction public: INSTRUCTION_HEADER(BitXor) static MBitXor *New(MDefinition *left, MDefinition *right); + static MBitXor *NewAsmJS(MDefinition *left, MDefinition *right); MDefinition *foldIfZero(size_t operand) { return getOperand(1 - operand); // 0 ^ x => x @@ -2450,6 +2542,7 @@ class MLsh : public MShiftInstruction public: INSTRUCTION_HEADER(Lsh) static MLsh *New(MDefinition *left, MDefinition *right); + static MLsh *NewAsmJS(MDefinition *left, MDefinition *right); MDefinition *foldIfZero(size_t operand) { // 0 << x => 0 @@ -2469,6 +2562,7 @@ class MRsh : public MShiftInstruction public: INSTRUCTION_HEADER(Rsh) static MRsh *New(MDefinition *left, MDefinition *right); + static MRsh *NewAsmJS(MDefinition *left, MDefinition *right); MDefinition *foldIfZero(size_t operand) { // 0 >> x => 0 @@ -2490,6 +2584,7 @@ class MUrsh : public MShiftInstruction public: INSTRUCTION_HEADER(Ursh) static MUrsh *New(MDefinition *left, MDefinition *right); + static MUrsh *NewAsmJS(MDefinition *left, MDefinition *right); MDefinition *foldIfZero(size_t operand) { // 0 >>> x => 0 @@ -2632,8 +2727,11 @@ class MAbs : public MUnaryInstruction, public ArithPolicy { + bool implicitTruncate_; + MAbs(MDefinition *num, MIRType type) - : MUnaryInstruction(num) + : MUnaryInstruction(num), + implicitTruncate_(false) { JS_ASSERT(type == MIRType_Double || type == MIRType_Int32); setResultType(type); @@ -2646,6 +2744,11 @@ class MAbs static MAbs *New(MDefinition *num, MIRType type) { return new MAbs(num, type); } + static MAbs *NewAsmJS(MDefinition *num, MIRType type) { + MAbs *ins = new MAbs(num, type); + ins->implicitTruncate_ = true; + return ins; + } MDefinition *num() const { return getOperand(0); } @@ -2853,6 +2956,16 @@ class MAdd : public MBinaryArithInstruction return new MAdd(left, right); } + static MAdd *NewAsmJS(MDefinition *left, MDefinition *right, MIRType type) { + MAdd *add = new MAdd(left, right); + add->specialization_ = type; + add->setResultType(type); + if (type == MIRType_Int32) + add->setTruncated(true); + return add; + } + void analyzeTruncateBackward(); + double getIdentity() { return 0; } @@ -2876,6 +2989,14 @@ class MSub : public MBinaryArithInstruction static MSub *New(MDefinition *left, MDefinition *right) { return new MSub(left, right); } + static MSub *NewAsmJS(MDefinition *left, MDefinition *right, MIRType type) { + MSub *sub = new MSub(left, right); + sub->specialization_ = type; + sub->setResultType(type); + if (type == MIRType_Int32) + sub->setTruncated(true); + return sub; + } double getIdentity() { return 0; @@ -2984,6 +3105,12 @@ class MDiv : public MBinaryArithInstruction static MDiv *New(MDefinition *left, MDefinition *right, MIRType type) { return new MDiv(left, right, type); } + static MDiv *NewAsmJS(MDefinition *left, MDefinition *right, MIRType type) { + MDiv *div = new MDiv(left, right, type); + if (type == MIRType_Int32) + div->setTruncated(true); + return div; + } MDefinition *foldsTo(bool useValueNumbers); void analyzeEdgeCasesForward(); @@ -3015,16 +3142,24 @@ class MDiv : public MBinaryArithInstruction class MMod : public MBinaryArithInstruction { - MMod(MDefinition *left, MDefinition *right) + MMod(MDefinition *left, MDefinition *right, MIRType type) : MBinaryArithInstruction(left, right) { - setResultType(MIRType_Value); + if (type != MIRType_Value) + specialization_ = type; + setResultType(type); } public: INSTRUCTION_HEADER(Mod) static MMod *New(MDefinition *left, MDefinition *right) { - return new MMod(left, right); + return new MMod(left, right, MIRType_Value); + } + static MMod *NewAsmJS(MDefinition *left, MDefinition *right, MIRType type) { + MMod *mod = new MMod(left, right, type); + if (type == MIRType_Int32) + mod->setTruncated(true); + return mod; } MDefinition *foldsTo(bool useValueNumbers); @@ -3910,7 +4045,16 @@ class MNot setMovable(); } - INSTRUCTION_HEADER(Not) + static MNot *New(MDefinition *elements) { + return new MNot(elements); + } + static MNot *NewAsmJS(MDefinition *elements) { + MNot *ins = new MNot(elements); + ins->setResultType(MIRType_Int32); + return ins; + } + + INSTRUCTION_HEADER(Not); void infer(const TypeOracle::UnaryTypes &u, JSContext *cx); MDefinition *foldsTo(bool useValueNumbers); @@ -4525,6 +4669,42 @@ class MStoreTypedArrayElement } }; +// Compute an "effective address", i.e., a compound computation of the form: +// base + index * scale + displacement +class MEffectiveAddress : public MBinaryInstruction +{ + MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement) + : MBinaryInstruction(base, index), scale_(scale), displacement_(displacement) + { + JS_ASSERT(base->type() == MIRType_Int32); + JS_ASSERT(index->type() == MIRType_Int32); + setMovable(); + setResultType(MIRType_Int32); + } + + Scale scale_; + int32_t displacement_; + + public: + INSTRUCTION_HEADER(EffectiveAddress); + + static MEffectiveAddress *New(MDefinition *base, MDefinition *index, Scale s, int32_t d) { + return new MEffectiveAddress(base, index, s, d); + } + MDefinition *base() const { + return lhs(); + } + MDefinition *index() const { + return rhs(); + } + Scale scale() const { + return scale_; + } + int32_t displacement() const { + return displacement_; + } +}; + // Clamp input to range [0, 255] for Uint8ClampedArray. class MClampToUint8 : public MUnaryInstruction, @@ -6616,6 +6796,347 @@ class FlattenedMResumePointIter } }; +class MAsmJSNeg : public MUnaryInstruction +{ + MAsmJSNeg(MDefinition *op, MIRType type) + : MUnaryInstruction(op) + { + setResultType(type); + setMovable(); + } + + public: + INSTRUCTION_HEADER(AsmJSNeg); + static MAsmJSNeg *NewAsmJS(MDefinition *op, MIRType type) { + return new MAsmJSNeg(op, type); + } + + MDefinition *input() const { + return getOperand(0); + } +}; + +class MAsmJSUDiv : public MBinaryInstruction +{ + MAsmJSUDiv(MDefinition *left, MDefinition *right) + : MBinaryInstruction(left, right) + { + setResultType(MIRType_Int32); + setMovable(); + } + + public: + INSTRUCTION_HEADER(AsmJSUDiv); + static MAsmJSUDiv *New(MDefinition *left, MDefinition *right) { + return new MAsmJSUDiv(left, right); + } +}; + +class MAsmJSUMod : public MBinaryInstruction +{ + MAsmJSUMod(MDefinition *left, MDefinition *right) + : MBinaryInstruction(left, right) + { + setResultType(MIRType_Int32); + setMovable(); + } + + public: + INSTRUCTION_HEADER(AsmJSUMod); + static MAsmJSUMod *New(MDefinition *left, MDefinition *right) { + return new MAsmJSUMod(left, right); + } +}; + +class MAsmJSLoadHeap : public MUnaryInstruction +{ + MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr) + : MUnaryInstruction(ptr), viewType_(vt) + { + if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64) + setResultType(MIRType_Double); + else + setResultType(MIRType_Int32); + } + + ArrayBufferView::ViewType viewType_; + + public: + INSTRUCTION_HEADER(AsmJSLoadHeap); + + static MAsmJSLoadHeap *New(ArrayBufferView::ViewType vt, MDefinition *ptr) { + return new MAsmJSLoadHeap(vt, ptr); + } + + ArrayBufferView::ViewType viewType() const { return viewType_; } + MDefinition *ptr() const { return getOperand(0); } +}; + +class MAsmJSStoreHeap : public MBinaryInstruction +{ + MAsmJSStoreHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v) + : MBinaryInstruction(ptr, v), viewType_(vt) + {} + + ArrayBufferView::ViewType viewType_; + + public: + INSTRUCTION_HEADER(AsmJSStoreHeap); + + static MAsmJSStoreHeap *New(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v) { + return new MAsmJSStoreHeap(vt, ptr, v); + } + + ArrayBufferView::ViewType viewType() const { return viewType_; } + MDefinition *ptr() const { return getOperand(0); } + MDefinition *value() const { return getOperand(1); } +}; + +class MAsmJSLoadGlobalVar : public MNullaryInstruction +{ + MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset) + : globalDataOffset_(globalDataOffset) + { + JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + setResultType(type); + } + + unsigned globalDataOffset_; + + public: + INSTRUCTION_HEADER(AsmJSLoadGlobalVar); + + static MAsmJSLoadGlobalVar *New(MIRType type, unsigned globalDataOffset) { + return new MAsmJSLoadGlobalVar(type, globalDataOffset); + } + + unsigned globalDataOffset() const { return globalDataOffset_; } +}; + +class MAsmJSStoreGlobalVar : public MUnaryInstruction +{ + MAsmJSStoreGlobalVar(unsigned globalDataOffset, MDefinition *v) + : MUnaryInstruction(v), globalDataOffset_(globalDataOffset) + {} + + unsigned globalDataOffset_; + + public: + INSTRUCTION_HEADER(AsmJSStoreGlobalVar); + + static MAsmJSStoreGlobalVar *New(unsigned globalDataOffset, MDefinition *v) { + return new MAsmJSStoreGlobalVar(globalDataOffset, v); + } + + unsigned globalDataOffset() const { return globalDataOffset_; } + MDefinition *value() const { return getOperand(0); } +}; + +class MAsmJSLoadFuncPtr : public MUnaryInstruction +{ + MAsmJSLoadFuncPtr(unsigned globalDataOffset, MDefinition *index) + : MUnaryInstruction(index), globalDataOffset_(globalDataOffset) + { + setResultType(MIRType_Pointer); + } + + unsigned globalDataOffset_; + + public: + INSTRUCTION_HEADER(AsmJSLoadFuncPtr); + + static MAsmJSLoadFuncPtr *New(unsigned globalDataOffset, MDefinition *index) { + return new MAsmJSLoadFuncPtr(globalDataOffset, index); + } + + unsigned globalDataOffset() const { return globalDataOffset_; } + MDefinition *index() const { return getOperand(0); } +}; + +class MAsmJSLoadFFIFunc : public MNullaryInstruction +{ + MAsmJSLoadFFIFunc(unsigned globalDataOffset) + : globalDataOffset_(globalDataOffset) + { + setResultType(MIRType_Pointer); + } + + unsigned globalDataOffset_; + + public: + INSTRUCTION_HEADER(AsmJSLoadFFIFunc); + + static MAsmJSLoadFFIFunc *New(unsigned globalDataOffset) { + return new MAsmJSLoadFFIFunc(globalDataOffset); + } + + unsigned globalDataOffset() const { return globalDataOffset_; } +}; + +class MAsmJSParameter : public MNullaryInstruction +{ + ABIArg abi_; + + MAsmJSParameter(ABIArg abi, MIRType mirType) + : abi_(abi) + { + setResultType(mirType); + } + + public: + INSTRUCTION_HEADER(AsmJSParameter); + + static MAsmJSParameter *New(ABIArg abi, MIRType mirType) { + return new MAsmJSParameter(abi, mirType); + } + + ABIArg abi() const { return abi_; } +}; + +class MAsmJSReturn : public MAryControlInstruction<1, 0> +{ + MAsmJSReturn(MDefinition *ins) { + setOperand(0, ins); + } + + public: + INSTRUCTION_HEADER(AsmJSReturn); + static MAsmJSReturn *New(MDefinition *ins) { + return new MAsmJSReturn(ins); + } +}; + +class MAsmJSVoidReturn : public MAryControlInstruction<0, 0> +{ + public: + INSTRUCTION_HEADER(AsmJSVoidReturn); + static MAsmJSVoidReturn *New() { + return new MAsmJSVoidReturn(); + } +}; + +class MAsmJSPassStackArg : public MUnaryInstruction +{ + MAsmJSPassStackArg(uint32_t spOffset, MDefinition *ins) + : MUnaryInstruction(ins), + spOffset_(spOffset) + {} + + uint32_t spOffset_; + + public: + INSTRUCTION_HEADER(AsmJSPassStackArg); + static MAsmJSPassStackArg *New(uint32_t spOffset, MDefinition *ins) { + return new MAsmJSPassStackArg(spOffset, ins); + } + uint32_t spOffset() const { + return spOffset_; + } + void incrementOffset(uint32_t inc) { + spOffset_ += inc; + } + MDefinition *arg() const { + return getOperand(0); + } +}; + +class MAsmJSCall : public MInstruction +{ + public: + class Callee { + public: + enum Which { Internal, Dynamic, Builtin }; + private: + Which which_; + union { + Label *internal_; + MDefinition *dynamic_; + const void *builtin_; + } u; + public: + Callee() {} + Callee(Label *callee) : which_(Internal) { u.internal_ = callee; } + Callee(MDefinition *callee) : which_(Dynamic) { u.dynamic_ = callee; } + Callee(const void *callee) : which_(Builtin) { u.builtin_ = callee; } + Which which() const { return which_; } + Label *internal() const { JS_ASSERT(which_ == Internal); return u.internal_; } + MDefinition *dynamic() const { JS_ASSERT(which_ == Dynamic); return u.dynamic_; } + const void *builtin() const { JS_ASSERT(which_ == Builtin); return u.builtin_; } + }; + + private: + struct Operand { + AnyRegister reg; + MUse use; + }; + + Callee callee_; + size_t numOperands_; + MUse *operands_; + size_t numArgs_; + AnyRegister *argRegs_; + size_t spIncrement_; + + protected: + void setOperand(size_t index, MDefinition *operand) { + operands_[index].set(operand, this, index); + operand->addUse(&operands_[index]); + } + MUse *getUseFor(size_t index) { + return &operands_[index]; + } + + public: + INSTRUCTION_HEADER(AsmJSCall); + + struct Arg { + AnyRegister reg; + MDefinition *def; + Arg(AnyRegister reg, MDefinition *def) : reg(reg), def(def) {} + }; + typedef Vector Args; + + static MAsmJSCall *New(Callee callee, const Args &args, MIRType resultType, size_t spIncrement); + + size_t numOperands() const { + return numOperands_; + } + MDefinition *getOperand(size_t index) const { + JS_ASSERT(index < numOperands_); + return operands_[index].producer(); + } + size_t numArgs() const { + return numArgs_; + } + AnyRegister registerForArg(size_t index) const { + JS_ASSERT(index < numArgs_); + return argRegs_[index]; + } + Callee callee() const { + return callee_; + } + size_t dynamicCalleeOperandIndex() const { + JS_ASSERT(callee_.which() == Callee::Dynamic); + JS_ASSERT(numArgs_ == numOperands_ - 1); + return numArgs_; + } + size_t spIncrement() const { + return spIncrement_; + } +}; + +// The asm.js version doesn't use the bail mechanism: instead it throws and +// exception by jumping to the given label. +class MAsmJSCheckOverRecursed : public MNullaryInstruction +{ + Label *onError_; + MAsmJSCheckOverRecursed(Label *onError) : onError_(onError) {} + public: + INSTRUCTION_HEADER(AsmJSCheckOverRecursed); + static MAsmJSCheckOverRecursed *New(Label *onError) { return new MAsmJSCheckOverRecursed(onError); } + Label *onError() const { return onError_; } +}; + #undef INSTRUCTION_HEADER // Implement opcode casts now that the compiler can see the inheritance. diff --git a/js/src/ion/MIRGenerator.h b/js/src/ion/MIRGenerator.h index 7041d6b798e..39e78b5b33f 100644 --- a/js/src/ion/MIRGenerator.h +++ b/js/src/ion/MIRGenerator.h @@ -17,6 +17,7 @@ #include "IonAllocPolicy.h" #include "IonCompartment.h" #include "CompileInfo.h" +#include "RegisterSets.h" namespace js { namespace ion { @@ -25,6 +26,16 @@ class MBasicBlock; class MIRGraph; class MStart; +struct AsmJSGlobalAccess +{ + unsigned offset; + unsigned globalDataOffset; + + AsmJSGlobalAccess(unsigned offset, unsigned globalDataOffset) + : offset(offset), globalDataOffset(globalDataOffset) + {} +}; + class MIRGenerator { public: @@ -72,6 +83,45 @@ class MIRGenerator cancelBuild_ = 1; } + bool compilingAsmJS() const { + return info_->script() == NULL; + } + + uint32_t maxAsmJSStackArgBytes() const { + JS_ASSERT(compilingAsmJS()); + return maxAsmJSStackArgBytes_; + } + uint32_t resetAsmJSMaxStackArgBytes() { + JS_ASSERT(compilingAsmJS()); + uint32_t old = maxAsmJSStackArgBytes_; + maxAsmJSStackArgBytes_ = 0; + return old; + } + void setAsmJSMaxStackArgBytes(uint32_t n) { + JS_ASSERT(compilingAsmJS()); + maxAsmJSStackArgBytes_ = n; + } + void setPerformsAsmJSCall() { + JS_ASSERT(compilingAsmJS()); + performsAsmJSCall_ = true; + } + bool performsAsmJSCall() const { + JS_ASSERT(compilingAsmJS()); + return performsAsmJSCall_; + } + bool noteHeapAccess(AsmJSHeapAccess heapAccess) { + return asmJSHeapAccesses_.append(heapAccess); + } + const Vector &heapAccesses() const { + return asmJSHeapAccesses_; + } + bool noteGlobalAccess(unsigned offset, unsigned globalDataOffset) { + return asmJSGlobalAccesses_.append(AsmJSGlobalAccess(offset, globalDataOffset)); + } + const Vector &globalAccesses() const { + return asmJSGlobalAccesses_; + } + public: JSCompartment *compartment; @@ -83,6 +133,11 @@ class MIRGenerator MIRGraph *graph_; bool error_; size_t cancelBuild_; + + uint32_t maxAsmJSStackArgBytes_; + bool performsAsmJSCall_; + Vector asmJSHeapAccesses_; + Vector asmJSGlobalAccesses_; }; } // namespace ion diff --git a/js/src/ion/MIRGraph.cpp b/js/src/ion/MIRGraph.cpp index 3a006847e7e..a155bfe0bd8 100644 --- a/js/src/ion/MIRGraph.cpp +++ b/js/src/ion/MIRGraph.cpp @@ -23,7 +23,11 @@ MIRGenerator::MIRGenerator(JSCompartment *compartment, temp_(temp), graph_(graph), error_(false), - cancelBuild_(0) + cancelBuild_(0), + maxAsmJSStackArgBytes_(0), + performsAsmJSCall_(false), + asmJSHeapAccesses_(GetIonContext()->cx), + asmJSGlobalAccesses_(GetIonContext()->cx) { } bool @@ -226,23 +230,27 @@ MBasicBlock::inherit(MBasicBlock *pred, uint32_t popped) stackPosition_ -= popped; if (kind_ != PENDING_LOOP_HEADER) copySlots(pred); - } else { + } else if (pc()) { uint32_t stackDepth = info().script()->analysis()->getCode(pc()).stackDepth; stackPosition_ = info().firstStackSlot() + stackDepth; JS_ASSERT(stackPosition_ >= popped); stackPosition_ -= popped; + } else { + stackPosition_ = info().firstStackSlot(); } JS_ASSERT(info_.nslots() >= stackPosition_); JS_ASSERT(!entryResumePoint_); - // Propagate the caller resume point from the inherited block. - MResumePoint *callerResumePoint = pred ? pred->callerResumePoint() : NULL; + if (pc()) { + // Propagate the caller resume point from the inherited block. + MResumePoint *callerResumePoint = pred ? pred->callerResumePoint() : NULL; - // Create a resume point using our initial stack state. - entryResumePoint_ = new MResumePoint(this, pc(), callerResumePoint, MResumePoint::ResumeAt); - if (!entryResumePoint_->init()) - return false; + // Create a resume point using our initial stack state. + entryResumePoint_ = new MResumePoint(this, pc(), callerResumePoint, MResumePoint::ResumeAt); + if (!entryResumePoint_->init()) + return false; + } if (pred) { if (!predecessors_.append(pred)) @@ -256,9 +264,10 @@ MBasicBlock::inherit(MBasicBlock *pred, uint32_t popped) phi->setOperand(0, pred->getSlot(i)); addPhi(phi); setSlot(i, phi); - entryResumePoint()->setOperand(i, phi); + if (entryResumePoint()) + entryResumePoint()->setOperand(i, phi); } - } else { + } else if (entryResumePoint()) { for (size_t i = 0; i < stackDepth(); i++) entryResumePoint()->setOperand(i, getSlot(i)); } @@ -313,7 +322,8 @@ void MBasicBlock::initSlot(uint32_t slot, MDefinition *ins) { slots_[slot] = ins; - entryResumePoint()->setOperand(slot, ins); + if (entryResumePoint()) + entryResumePoint()->setOperand(slot, ins); } void @@ -672,7 +682,8 @@ MBasicBlock::addPredecessorPopN(MBasicBlock *pred, uint32_t popped) phi->setOperand(predecessors_.length(), other); setSlot(i, phi); - entryResumePoint()->replaceOperand(i, phi); + if (entryResumePoint()) + entryResumePoint()->replaceOperand(i, phi); } } } @@ -719,7 +730,7 @@ MBasicBlock::setBackedge(MBasicBlock *pred) // Predecessors must be finished, and at the correct stack depth. JS_ASSERT(lastIns_); JS_ASSERT(pred->lastIns_); - JS_ASSERT(pred->stackDepth() == entryResumePoint()->stackDepth()); + JS_ASSERT_IF(entryResumePoint(), pred->stackDepth() == entryResumePoint()->stackDepth()); // We must be a pending loop header JS_ASSERT(kind_ == PENDING_LOOP_HEADER); diff --git a/js/src/ion/MIRGraph.h b/js/src/ion/MIRGraph.h index 44cb4319535..99818abc9bc 100644 --- a/js/src/ion/MIRGraph.h +++ b/js/src/ion/MIRGraph.h @@ -51,10 +51,6 @@ class MBasicBlock : public TempObject, public InlineListNode // Does this block do something that forces it to terminate early? bool earlyAbort_; - - // Sets a slot, taking care to rewrite copies. - void setSlot(uint32_t slot, MDefinition *ins); - // Pushes a copy of a local variable or argument. void pushVariable(uint32_t slot); @@ -126,6 +122,7 @@ class MBasicBlock : public TempObject, public InlineListNode void setLocal(uint32_t local); void setArg(uint32_t arg); void setSlot(uint32_t slot); + void setSlot(uint32_t slot, MDefinition *ins); // Rewrites a slot directly, bypassing the stack transition. This should // not be used under most circumstances. diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index 9c2133b3acd..1053f52dea7 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -122,6 +122,7 @@ namespace ion { _(LoadTypedArrayElement) \ _(LoadTypedArrayElementHole) \ _(StoreTypedArrayElement) \ + _(EffectiveAddress) \ _(ClampToUint8) \ _(LoadFixedSlot) \ _(StoreFixedSlot) \ @@ -150,6 +151,22 @@ namespace ion { _(FunctionBoundary) \ _(GetDOMProperty) \ _(SetDOMProperty) \ + _(AsmJSNeg) \ + _(AsmJSUDiv) \ + _(AsmJSUMod) \ + _(AsmJSUnsignedToDouble) \ + _(AsmJSLoadHeap) \ + _(AsmJSStoreHeap) \ + _(AsmJSLoadGlobalVar) \ + _(AsmJSStoreGlobalVar) \ + _(AsmJSLoadFuncPtr) \ + _(AsmJSLoadFFIFunc) \ + _(AsmJSReturn) \ + _(AsmJSParameter) \ + _(AsmJSVoidReturn) \ + _(AsmJSPassStackArg) \ + _(AsmJSCall) \ + _(AsmJSCheckOverRecursed) \ _(ParCheckOverRecursed) \ _(ParNewCallObject) \ _(ParNew) \ diff --git a/js/src/ion/MoveResolver.h b/js/src/ion/MoveResolver.h index 7ac1360c030..0f816e15be5 100644 --- a/js/src/ion/MoveResolver.h +++ b/js/src/ion/MoveResolver.h @@ -201,6 +201,9 @@ class MoveResolver bool hasCycles() const { return hasCycles_; } + void clearTempObjectPool() { + movePool_.clear(); + } }; } // namespace ion diff --git a/js/src/ion/ParallelArrayAnalysis.cpp b/js/src/ion/ParallelArrayAnalysis.cpp index aa8d3937ca8..ed7ab81495f 100644 --- a/js/src/ion/ParallelArrayAnalysis.cpp +++ b/js/src/ion/ParallelArrayAnalysis.cpp @@ -264,6 +264,23 @@ class ParallelArrayVisitor : public MInstructionVisitor SAFE_OP(ParCheckInterrupt) SAFE_OP(ParCheckOverRecursed) SAFE_OP(PolyInlineDispatch) + UNSAFE_OP(EffectiveAddress) + UNSAFE_OP(AsmJSUnsignedToDouble) + UNSAFE_OP(AsmJSNeg) + UNSAFE_OP(AsmJSUDiv) + UNSAFE_OP(AsmJSUMod) + UNSAFE_OP(AsmJSLoadHeap) + UNSAFE_OP(AsmJSStoreHeap) + UNSAFE_OP(AsmJSLoadGlobalVar) + UNSAFE_OP(AsmJSStoreGlobalVar) + UNSAFE_OP(AsmJSLoadFuncPtr) + UNSAFE_OP(AsmJSLoadFFIFunc) + UNSAFE_OP(AsmJSReturn) + UNSAFE_OP(AsmJSVoidReturn) + UNSAFE_OP(AsmJSPassStackArg) + UNSAFE_OP(AsmJSParameter) + UNSAFE_OP(AsmJSCall) + UNSAFE_OP(AsmJSCheckOverRecursed) // It looks like this could easily be made safe: UNSAFE_OP(ConvertElementsToDoubles) diff --git a/js/src/ion/RangeAnalysis.cpp b/js/src/ion/RangeAnalysis.cpp index 1817ac2de8b..b585c498357 100644 --- a/js/src/ion/RangeAnalysis.cpp +++ b/js/src/ion/RangeAnalysis.cpp @@ -141,6 +141,10 @@ RangeAnalysis::addBetaNobes() MCompare *compare = test->getOperand(0)->toCompare(); + // TODO: support unsigned comparisons + if (compare->compareType() == MCompare::Compare_UInt32) + continue; + MDefinition *left = compare->getOperand(0); MDefinition *right = compare->getOperand(1); int32_t bound; @@ -703,7 +707,7 @@ MSub::computeRange() void MMul::computeRange() { - if (specialization() != MIRType_Int32 && specialization() != MIRType_Double) + if ((specialization() != MIRType_Int32 && specialization() != MIRType_Double) || isTruncated()) return; Range left(getOperand(0)); Range right(getOperand(1)); diff --git a/js/src/ion/RegisterAllocator.h b/js/src/ion/RegisterAllocator.h index ad9cecf946d..f3e35559e41 100644 --- a/js/src/ion/RegisterAllocator.h +++ b/js/src/ion/RegisterAllocator.h @@ -309,6 +309,10 @@ class RegisterAllocator { if (FramePointer != InvalidReg && lir->mir()->instrumentedProfiling()) allRegisters_.take(AnyRegister(FramePointer)); +#ifdef JS_CPU_X64 + if (mir->compilingAsmJS()) + allRegisters_.take(AnyRegister(HeapReg)); +#endif } protected: diff --git a/js/src/ion/RegisterSets.h b/js/src/ion/RegisterSets.h index 73b21eebe9a..681e4989731 100644 --- a/js/src/ion/RegisterSets.h +++ b/js/src/ion/RegisterSets.h @@ -654,6 +654,92 @@ class AnyRegisterIterator } }; +class ABIArg +{ + public: + enum Kind { GPR, FPU, Stack }; + + private: + Kind kind_; + union { + Registers::Code gpr_; + FloatRegisters::Code fpu_; + uint32_t offset_; + } u; + + public: + ABIArg() : kind_(Kind(-1)) { u.offset_ = -1; } + ABIArg(Register gpr) : kind_(GPR) { u.gpr_ = gpr.code(); } + ABIArg(FloatRegister fpu) : kind_(FPU) { u.fpu_ = fpu.code(); } + ABIArg(uint32_t offset) : kind_(Stack) { u.offset_ = offset; } + + Kind kind() const { return kind_; } + Register gpr() const { JS_ASSERT(kind() == GPR); return Register::FromCode(u.gpr_); } + FloatRegister fpu() const { JS_ASSERT(kind() == FPU); return FloatRegister::FromCode(u.fpu_); } + uint32_t offsetFromArgBase() const { JS_ASSERT(kind() == Stack); return u.offset_; } + + bool argInRegister() const { return kind() != Stack; } + AnyRegister reg() const { return kind_ == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); } +}; + +class AsmJSHeapAccess +{ + uint32_t offset_; + uint8_t opLength_; +#if defined(JS_CPU_X86) + uint8_t cmpDelta_; +#endif + uint8_t isFloat32Load_; + ion::AnyRegister::Code loadedReg_ : 8; + + JS_STATIC_ASSERT(ion::AnyRegister::Total < UINT8_MAX); + + public: +#if defined(JS_CPU_X86) + AsmJSHeapAccess(uint32_t cmp, uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt, + AnyRegister loadedReg) + : offset_(offset), + opLength_(after - offset), + cmpDelta_(offset - cmp), + isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32), + loadedReg_(loadedReg.code()) + {} + AsmJSHeapAccess(uint32_t cmp, uint32_t offset, uint8_t after) + : offset_(offset), + opLength_(after - offset), + cmpDelta_(offset - cmp), + isFloat32Load_(false), + loadedReg_(UINT8_MAX) + {} +#else + AsmJSHeapAccess(uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt, + AnyRegister loadedReg) + : offset_(offset), + opLength_(after - offset), + isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32), + loadedReg_(loadedReg.code()) + {} + AsmJSHeapAccess(uint32_t offset, uint8_t after) + : offset_(offset), + opLength_(after - offset), + isFloat32Load_(false), + loadedReg_(UINT8_MAX) + {} +#endif + + uint32_t offset() const { return offset_; } + unsigned opLength() const { return opLength_; } + bool isLoad() const { return loadedReg_ != UINT8_MAX; } + bool isFloat32Load() const { return isFloat32Load_; } + ion::AnyRegister loadedReg() const { return ion::AnyRegister::FromCode(loadedReg_); } + +#if defined(JS_CPU_X86) + void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); } + void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); } +#endif + void updateOffset(uint32_t offset) { offset_ = offset; } +}; + } // namespace ion } // namespace js diff --git a/js/src/ion/Safepoints.cpp b/js/src/ion/Safepoints.cpp index cb6203e1974..180b929d030 100644 --- a/js/src/ion/Safepoints.cpp +++ b/js/src/ion/Safepoints.cpp @@ -423,7 +423,7 @@ PartFromStream(CompactBufferReader &stream, NunboxPartKind kind, uint32_t info) return LStackSlot(info); JS_ASSERT(kind == Part_Arg); - return LArgument(info); + return LArgument(LAllocation::INT_ARGUMENT, info); } bool diff --git a/js/src/ion/StupidAllocator.cpp b/js/src/ion/StupidAllocator.cpp index e85fed83c6e..b5b3fb7543f 100644 --- a/js/src/ion/StupidAllocator.cpp +++ b/js/src/ion/StupidAllocator.cpp @@ -24,7 +24,7 @@ LAllocation * StupidAllocator::stackLocation(uint32_t vreg) { LDefinition *def = virtualRegisters[vreg]; - if (def->policy() == LDefinition::PRESET && def->output()->kind() == LAllocation::ARGUMENT) + if (def->policy() == LDefinition::PRESET && def->output()->isArgument()) return def->output(); return new LStackSlot(DefaultStackSlot(vreg), def->type() == LDefinition::DOUBLE); diff --git a/js/src/ion/TypeOracle.h b/js/src/ion/TypeOracle.h index c5aa3d91e60..a09d8c51339 100644 --- a/js/src/ion/TypeOracle.h +++ b/js/src/ion/TypeOracle.h @@ -372,8 +372,8 @@ StringFromMIRType(MIRType type) return "Slots"; case MIRType_Elements: return "Elements"; - case MIRType_StackFrame: - return "StackFrame"; + case MIRType_Pointer: + return "Pointer"; case MIRType_ForkJoinSlice: return "ForkJoinSlice"; default: diff --git a/js/src/ion/arm/Assembler-arm.cpp b/js/src/ion/arm/Assembler-arm.cpp index c25f6cd1bf5..74bb04829a7 100644 --- a/js/src/ion/arm/Assembler-arm.cpp +++ b/js/src/ion/arm/Assembler-arm.cpp @@ -751,11 +751,11 @@ Assembler::trace(JSTracer *trc) } void -Assembler::processCodeLabels(IonCode *code) +Assembler::processCodeLabels(uint8_t *rawCode) { for (size_t i = 0; i < codeLabels_.length(); i++) { CodeLabel label = codeLabels_[i]; - Bind(code, label.dest(), code->raw() + actualOffset(label.src()->offset())); + Bind(rawCode, label.dest(), rawCode + actualOffset(label.src()->offset())); } } @@ -774,12 +774,11 @@ Assembler::writeCodePointer(AbsoluteLabel *absoluteLabel) { } void -Assembler::Bind(IonCode *code, AbsoluteLabel *label, const void *address) +Assembler::Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address) { // See writeCodePointer comment. - uint8_t *raw = code->raw(); uint32_t off = actualOffset(label->offset()); - *reinterpret_cast(raw + off) = address; + *reinterpret_cast(rawCode + off) = address; } Assembler::Condition diff --git a/js/src/ion/arm/Assembler-arm.h b/js/src/ion/arm/Assembler-arm.h index f2aee8b4553..26a09b8fcac 100644 --- a/js/src/ion/arm/Assembler-arm.h +++ b/js/src/ion/arm/Assembler-arm.h @@ -98,6 +98,8 @@ static const FloatRegister d15 = {FloatRegisters::d15}; // function boundaries. I'm trying to make sure this is always true. static const uint32_t StackAlignment = 8; static const bool StackKeptAligned = true; +static const uint32_t NativeFrameSize = sizeof(void*); +static const uint32_t AlignmentAtPrologue = sizeof(void*); static const Scale ScalePointer = TimesFour; @@ -1299,7 +1301,7 @@ class Assembler public: void finish(); void executableCopy(void *buffer); - void processCodeLabels(IonCode *code); + void processCodeLabels(uint8_t *rawCode); void copyJumpRelocationTable(uint8_t *dest); void copyDataRelocationTable(uint8_t *dest); void copyPreBarrierTable(uint8_t *dest); @@ -1545,7 +1547,7 @@ class Assembler void retarget(Label *label, Label *target); // I'm going to pretend this doesn't exist for now. void retarget(Label *label, void *target, Relocation::Kind reloc); - void Bind(IonCode *code, AbsoluteLabel *label, const void *address); + void Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address); void call(Label *label); void call(void *target); diff --git a/js/src/ion/arm/CodeGenerator-arm.cpp b/js/src/ion/arm/CodeGenerator-arm.cpp index 901edab8aa8..8ff13700e18 100644 --- a/js/src/ion/arm/CodeGenerator-arm.cpp +++ b/js/src/ion/arm/CodeGenerator-arm.cpp @@ -27,8 +27,8 @@ using namespace js; using namespace js::ion; // shared -CodeGeneratorARM::CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph) - : CodeGeneratorShared(gen, graph), +CodeGeneratorARM::CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) + : CodeGeneratorShared(gen, graph, masm), deoptLabel_(NULL) { } @@ -106,6 +106,7 @@ CodeGeneratorARM::visitTestIAndBranch(LTestIAndBranch *test) bool CodeGeneratorARM::visitCompare(LCompare *comp) { + Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); const LAllocation *left = comp->getOperand(0); const LAllocation *right = comp->getOperand(1); const LDefinition *def = comp->getDef(0); @@ -115,14 +116,14 @@ CodeGeneratorARM::visitCompare(LCompare *comp) else masm.ma_cmp(ToRegister(left), ToOperand(right)); masm.ma_mov(Imm32(0), ToRegister(def)); - masm.ma_mov(Imm32(1), ToRegister(def), NoSetCond, JSOpToCondition(comp->jsop())); + masm.ma_mov(Imm32(1), ToRegister(def), NoSetCond, cond); return true; } bool CodeGeneratorARM::visitCompareAndBranch(LCompareAndBranch *comp) { - Assembler::Condition cond = JSOpToCondition(comp->jsop()); + Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); if (comp->right()->isConstant()) masm.ma_cmp(ToRegister(comp->left()), Imm32(ToInt32(comp->right()))); else @@ -1243,7 +1244,7 @@ CodeGeneratorARM::visitCompareB(LCompareB *lir) masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); else masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); - masm.emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output); masm.jump(&done); } @@ -1274,7 +1275,7 @@ CodeGeneratorARM::visitCompareBAndBranch(LCompareBAndBranch *lir) masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); else masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); - emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse()); + emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse()); return true; } @@ -1282,7 +1283,7 @@ bool CodeGeneratorARM::visitCompareV(LCompareV *lir) { MCompare *mir = lir->mir(); - Assembler::Condition cond = JSOpToCondition(mir->jsop()); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput); const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput); const Register output = ToRegister(lir->output()); @@ -1311,7 +1312,7 @@ bool CodeGeneratorARM::visitCompareVAndBranch(LCompareVAndBranch *lir) { MCompare *mir = lir->mir(); - Assembler::Condition cond = JSOpToCondition(mir->jsop()); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput); const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput); @@ -1331,6 +1332,14 @@ CodeGeneratorARM::visitCompareVAndBranch(LCompareVAndBranch *lir) return true; } + +bool +CodeGeneratorARM::visitUInt32ToDouble(LUInt32ToDouble *lir) +{ + masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + bool CodeGeneratorARM::visitNotI(LNotI *ins) { diff --git a/js/src/ion/arm/CodeGenerator-arm.h b/js/src/ion/arm/CodeGenerator-arm.h index 87ef62b6b70..4d9c777eb72 100644 --- a/js/src/ion/arm/CodeGenerator-arm.h +++ b/js/src/ion/arm/CodeGenerator-arm.h @@ -94,6 +94,7 @@ class CodeGeneratorARM : public CodeGeneratorShared virtual bool visitCompareBAndBranch(LCompareBAndBranch *lir); virtual bool visitCompareV(LCompareV *lir); virtual bool visitCompareVAndBranch(LCompareVAndBranch *lir); + virtual bool visitUInt32ToDouble(LUInt32ToDouble *lir); virtual bool visitNotI(LNotI *ins); virtual bool visitNotD(LNotD *ins); @@ -118,7 +119,7 @@ class CodeGeneratorARM : public CodeGeneratorShared const Register &elements, const LAllocation *index); public: - CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph); + CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); public: bool visitBox(LBox *box); @@ -142,6 +143,8 @@ class CodeGeneratorARM : public CodeGeneratorShared bool visitInterruptCheck(LInterruptCheck *lir); bool generateInvalidateEpilogue(); + + void postAsmJSCall(LAsmJSCall *lir) {} }; typedef CodeGeneratorARM CodeGeneratorSpecific; diff --git a/js/src/ion/arm/LIR-arm.h b/js/src/ion/arm/LIR-arm.h index 15e6b27e0ab..bd5e4197157 100644 --- a/js/src/ion/arm/LIR-arm.h +++ b/js/src/ion/arm/LIR-arm.h @@ -81,6 +81,17 @@ class LDouble : public LInstructionHelper<1, 1, 0> } }; +// Convert a 32-bit unsigned integer to a double. +class LUInt32ToDouble : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(UInt32ToDouble) + + LUInt32ToDouble(const LAllocation &input) { + setOperand(0, input); + } +}; + // LDivI is presently implemented as a proper C function, // so it trashes r0, r1, r2 and r3. The call also trashes lr, and has the // ability to trash ip. The function also takes two arguments (dividend in r0, diff --git a/js/src/ion/arm/LOpcodes-arm.h b/js/src/ion/arm/LOpcodes-arm.h index fe4c8ba4bee..4b313a70d01 100644 --- a/js/src/ion/arm/LOpcodes-arm.h +++ b/js/src/ion/arm/LOpcodes-arm.h @@ -17,7 +17,8 @@ _(ModI) \ _(ModPowTwoI) \ _(ModMaskI) \ - _(PowHalfD) + _(PowHalfD) \ + _(UInt32ToDouble) #endif // jsion_lir_opcodes_arm_h__ diff --git a/js/src/ion/arm/MacroAssembler-arm.h b/js/src/ion/arm/MacroAssembler-arm.h index 4cbad291cd8..c3fcbc1d1f0 100644 --- a/js/src/ion/arm/MacroAssembler-arm.h +++ b/js/src/ion/arm/MacroAssembler-arm.h @@ -726,6 +726,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void branchTestPtr(Condition cond, const Register &lhs, const Register &rhs, Label *label) { branchTest32(cond, lhs, rhs, label); } + void branchTestPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) { + branchTest32(cond, lhs, imm, label); + } void branchPtr(Condition cond, Register lhs, Register rhs, Label *label) { branch32(cond, lhs, rhs, label); } diff --git a/js/src/ion/shared/Assembler-shared.h b/js/src/ion/shared/Assembler-shared.h index d2630cf8cfe..16fbed99823 100644 --- a/js/src/ion/shared/Assembler-shared.h +++ b/js/src/ion/shared/Assembler-shared.h @@ -24,12 +24,31 @@ namespace js { namespace ion { enum Scale { - TimesOne, - TimesTwo, - TimesFour, - TimesEight + TimesOne = 0, + TimesTwo = 1, + TimesFour = 2, + TimesEight = 3 }; +static inline unsigned +ScaleToShift(Scale scale) +{ + return unsigned(scale); +} + +static inline bool +IsShiftInScaleRange(int i) +{ + return i >= TimesOne && i <= TimesEight; +} + +static inline Scale +ShiftToScale(int i) +{ + JS_ASSERT(IsShiftInScaleRange(i)); + return Scale(i); +} + static inline Scale ScaleFromElemWidth(int shift) { diff --git a/js/src/ion/shared/Assembler-x86-shared.cpp b/js/src/ion/shared/Assembler-x86-shared.cpp index 32689780df0..ebad4556896 100644 --- a/js/src/ion/shared/Assembler-x86-shared.cpp +++ b/js/src/ion/shared/Assembler-x86-shared.cpp @@ -91,11 +91,11 @@ AssemblerX86Shared::executableCopy(void *buffer) } void -AssemblerX86Shared::processCodeLabels(IonCode *code) +AssemblerX86Shared::processCodeLabels(uint8_t *rawCode) { for (size_t i = 0; i < codeLabels_.length(); i++) { CodeLabel label = codeLabels_[i]; - Bind(code, label.dest(), code->raw() + label.src()->offset()); + Bind(rawCode, label.dest(), rawCode + label.src()->offset()); } } diff --git a/js/src/ion/shared/Assembler-x86-shared.h b/js/src/ion/shared/Assembler-x86-shared.h index 1b6fc5cd7ee..f3204895760 100644 --- a/js/src/ion/shared/Assembler-x86-shared.h +++ b/js/src/ion/shared/Assembler-x86-shared.h @@ -165,7 +165,7 @@ class AssemblerX86Shared } void executableCopy(void *buffer); - void processCodeLabels(IonCode *code); + void processCodeLabels(uint8_t *rawCode); void copyJumpRelocationTable(uint8_t *dest); void copyDataRelocationTable(uint8_t *dest); void copyPreBarrierTable(uint8_t *dest); @@ -173,6 +173,9 @@ class AssemblerX86Shared bool addCodeLabel(CodeLabel label) { return codeLabels_.append(label); } + size_t numCodeLabels() const { + return codeLabels_.length(); + } // Size of the instruction stream, in bytes. size_t size() const { @@ -457,6 +460,18 @@ class AssemblerX86Shared JS_NOT_REACHED("unexpected operand kind"); } } + void leal(const Operand &src, const Register &dest) { + switch (src.kind()) { + case Operand::REG_DISP: + masm.leal_mr(src.disp(), src.base(), dest.code()); + break; + case Operand::SCALE: + masm.leal_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); + break; + default: + JS_NOT_REACHED("unexpected operand kind"); + } + } protected: JmpSrc jSrc(Condition cond, Label *label) { @@ -591,8 +606,7 @@ class AssemblerX86Shared label->reset(); } - static void Bind(IonCode *code, AbsoluteLabel *label, const void *address) { - uint8_t *raw = code->raw(); + static void Bind(uint8_t *raw, AbsoluteLabel *label, const void *address) { if (label->used()) { intptr_t src = label->offset(); do { @@ -1000,6 +1014,13 @@ class AssemblerX86Shared masm.pop_r(src.code()); } + void pushFlags() { + masm.push_flags(); + } + void popFlags() { + masm.pop_flags(); + } + #ifdef JS_CPU_X86 void pushAllRegs() { masm.pusha(); @@ -1017,8 +1038,11 @@ class AssemblerX86Shared void cdq() { masm.cdq(); } - void idiv(Register dest) { - masm.idivl_r(dest.code()); + void idiv(Register divisor) { + masm.idivl_r(divisor.code()); + } + void udiv(Register divisor) { + masm.divl_r(divisor.code()); } void unpcklps(const FloatRegister &src, const FloatRegister &dest) { diff --git a/js/src/ion/shared/CodeGenerator-shared-inl.h b/js/src/ion/shared/CodeGenerator-shared-inl.h index 1c0addc6814..7bf3b611417 100644 --- a/js/src/ion/shared/CodeGenerator-shared-inl.h +++ b/js/src/ion/shared/CodeGenerator-shared-inl.h @@ -21,6 +21,11 @@ ToInt32(const LAllocation *a) JS_NOT_REACHED("this is not a constant!"); return -1; } +static inline double +ToDouble(const LAllocation *a) +{ + return a->toConstant()->toNumber(); +} static inline Register ToRegister(const LAllocation &a) diff --git a/js/src/ion/shared/CodeGenerator-shared.cpp b/js/src/ion/shared/CodeGenerator-shared.cpp index a998d339f3e..17c29cac617 100644 --- a/js/src/ion/shared/CodeGenerator-shared.cpp +++ b/js/src/ion/shared/CodeGenerator-shared.cpp @@ -25,10 +25,20 @@ using mozilla::DebugOnly; namespace js { namespace ion { -CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph) +MacroAssembler & +CodeGeneratorShared::ensureMasm(MacroAssembler *masmArg) +{ + if (masmArg) + return *masmArg; + maybeMasm_.construct(); + return maybeMasm_.ref(); +} + +CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masmArg) : oolIns(NULL), oolParallelAbort_(NULL), - masm(&sps_), + maybeMasm_(), + masm(ensureMasm(masmArg)), gen(gen), graph(*graph), current(NULL), @@ -42,7 +52,31 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph) frameDepth_(graph->localSlotCount() * sizeof(STACK_SLOT_SIZE) + graph->argumentSlotCount() * sizeof(Value)) { - frameClass_ = FrameSizeClass::FromDepth(frameDepth_); + if (!gen->compilingAsmJS()) + masm.setInstrumentation(&sps_); + + // Since asm.js uses the system ABI which does not necessarily use a + // regular array where all slots are sizeof(Value), it maintains the max + // argument stack depth separately. + if (gen->compilingAsmJS()) { + JS_ASSERT(graph->argumentSlotCount() == 0); + frameDepth_ += gen->maxAsmJSStackArgBytes(); + + // An MAsmJSCall does not align the stack pointer at calls sites but instead + // relies on the a priori stack adjustment (in the prologue) on platforms + // (like x64) which require the stack to be aligned. + if (gen->performsAsmJSCall()) { + unsigned alignmentAtCall = AlignmentAtPrologue + frameDepth_; + if (unsigned rem = alignmentAtCall % StackAlignment) + frameDepth_ += StackAlignment - rem; + } + + // FrameSizeClass is only used for bailing, which cannot happen in + // asm.js code. + frameClass_ = FrameSizeClass::None(); + } else { + frameClass_ = FrameSizeClass::FromDepth(frameDepth_); + } } bool diff --git a/js/src/ion/shared/CodeGenerator-shared.h b/js/src/ion/shared/CodeGenerator-shared.h index a7f6fc0bf9f..f0bd0b88216 100644 --- a/js/src/ion/shared/CodeGenerator-shared.h +++ b/js/src/ion/shared/CodeGenerator-shared.h @@ -39,8 +39,11 @@ class CodeGeneratorShared : public LInstructionVisitor OutOfLineCode *oolIns; OutOfLineParallelAbort *oolParallelAbort_; + MacroAssembler &ensureMasm(MacroAssembler *masm); + mozilla::Maybe maybeMasm_; + public: - MacroAssembler masm; + MacroAssembler &masm; protected: MIRGenerator *gen; @@ -105,7 +108,9 @@ class CodeGeneratorShared : public LInstructionVisitor // For arguments to the current function. inline int32_t ArgToStackOffset(int32_t slot) const { - return masm.framePushed() + sizeof(IonJSFrameLayout) + slot; + return masm.framePushed() + + (gen->compilingAsmJS() ? NativeFrameSize : sizeof(IonJSFrameLayout)) + + slot; } // For the callee of the current function. @@ -311,13 +316,14 @@ class CodeGeneratorShared : public LInstructionVisitor protected: bool addOutOfLineCode(OutOfLineCode *code); + bool hasOutOfLineCode() { return !outOfLineCode_.empty(); } bool generateOutOfLineCode(); private: void generateInvalidateEpilogue(); public: - CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph); + CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); public: template diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.cpp b/js/src/ion/shared/CodeGenerator-x86-shared.cpp index 533f8802611..311df6d550e 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp @@ -23,8 +23,8 @@ using namespace js::ion; namespace js { namespace ion { -CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph) - : CodeGeneratorShared(gen, graph), +CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) + : CodeGeneratorShared(gen, graph, masm), deoptLabel_(NULL) { } @@ -147,16 +147,18 @@ CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type, const LAllocatio bool CodeGeneratorX86Shared::visitCompare(LCompare *comp) { - emitCompare(comp->mir()->compareType(), comp->left(), comp->right()); - masm.emitSet(JSOpToCondition(comp->jsop()), ToRegister(comp->output())); + MCompare *mir = comp->mir(); + emitCompare(mir->compareType(), comp->left(), comp->right()); + masm.emitSet(JSOpToCondition(mir->compareType(), comp->jsop()), ToRegister(comp->output())); return true; } bool CodeGeneratorX86Shared::visitCompareAndBranch(LCompareAndBranch *comp) { - emitCompare(comp->mir()->compareType(), comp->left(), comp->right()); - Assembler::Condition cond = JSOpToCondition(comp->jsop()); + MCompare *mir = comp->mir(); + emitCompare(mir->compareType(), comp->left(), comp->right()); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop()); emitBranch(cond, comp->ifTrue(), comp->ifFalse()); return true; } @@ -206,6 +208,22 @@ CodeGeneratorX86Shared::visitCompareDAndBranch(LCompareDAndBranch *comp) return true; } +bool +CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins) +{ + const MAsmJSPassStackArg *mir = ins->mir(); + Operand dst(StackPointer, mir->spOffset()); + if (ins->arg()->isConstant()) { + masm.mov(Imm32(ToInt32(ins->arg())), dst); + } else { + if (ins->arg()->isGeneralReg()) + masm.mov(ToRegister(ins->arg()), dst); + else + masm.movsd(ToFloatRegister(ins->arg()), dst); + } + return true; +} + bool CodeGeneratorX86Shared::generateOutOfLineCode() { @@ -614,6 +632,31 @@ CodeGeneratorX86Shared::visitMulI(LMulI *ins) return true; } +bool +CodeGeneratorX86Shared::visitAsmJSDivOrMod(LAsmJSDivOrMod *ins) +{ + JS_ASSERT(ToRegister(ins->remainder()) == edx); + JS_ASSERT(ToRegister(ins->lhs()) == eax); + Register rhs = ToRegister(ins->rhs()); + Register output = ToRegister(ins->output()); + + Label afterDiv; + + masm.testl(rhs, rhs); + Label notzero; + masm.j(Assembler::NonZero, ¬zero); + masm.movl(Imm32(0), output); + masm.jmp(&afterDiv); + masm.bind(¬zero); + + masm.xorl(edx, edx); + masm.udiv(rhs); + + masm.bind(&afterDiv); + + return true; +} + bool CodeGeneratorX86Shared::visitMulNegativeZeroCheck(MulNegativeZeroCheck *ool) { @@ -1312,6 +1355,17 @@ CodeGeneratorX86Shared::visitTruncateDToInt32(LTruncateDToInt32 *ins) return true; } +bool +CodeGeneratorX86Shared::visitEffectiveAddress(LEffectiveAddress *ins) +{ + const MEffectiveAddress *mir = ins->mir(); + Register base = ToRegister(ins->base()); + Register index = ToRegister(ins->index()); + Register output = ToRegister(ins->output()); + masm.leal(Operand(base, index, mir->scale(), mir->displacement()), output); + return true; +} + bool CodeGeneratorX86Shared::visitOutOfLineTruncate(OutOfLineTruncate *ool) { diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.h b/js/src/ion/shared/CodeGenerator-x86-shared.h index f3c24f32f57..74e58b796b2 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.h +++ b/js/src/ion/shared/CodeGenerator-x86-shared.h @@ -73,7 +73,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared bool emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, const Register &base); public: - CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph); + CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); public: // Instruction visitors. @@ -107,6 +107,9 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared virtual bool visitGuardShape(LGuardShape *guard); virtual bool visitGuardClass(LGuardClass *guard); virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins); + virtual bool visitEffectiveAddress(LEffectiveAddress *ins); + virtual bool visitAsmJSDivOrMod(LAsmJSDivOrMod *ins); + virtual bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins); // Out of line visitors. bool visitOutOfLineBailout(OutOfLineBailout *ool); diff --git a/js/src/ion/shared/LIR-x86-shared.h b/js/src/ion/shared/LIR-x86-shared.h index d928daaf07d..1549fff6e36 100644 --- a/js/src/ion/shared/LIR-x86-shared.h +++ b/js/src/ion/shared/LIR-x86-shared.h @@ -49,6 +49,24 @@ class LModI : public LBinaryMath<1> } }; +// This class performs a simple x86 'div', yielding either a quotient or remainder depending on +// whether this instruction is defined to output eax (quotient) or edx (remainder). +class LAsmJSDivOrMod : public LBinaryMath<1> +{ + public: + LIR_HEADER(AsmJSDivOrMod); + + LAsmJSDivOrMod(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp) { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, temp); + } + + const LDefinition *remainder() { + return getTemp(0); + } +}; + class LModPowTwoI : public LInstructionHelper<1,1,0> { const int32_t shift_; diff --git a/js/src/ion/shared/Lowering-shared-inl.h b/js/src/ion/shared/Lowering-shared-inl.h index b910220577d..42eaadc9823 100644 --- a/js/src/ion/shared/Lowering-shared-inl.h +++ b/js/src/ion/shared/Lowering-shared-inl.h @@ -114,8 +114,8 @@ LIRGeneratorShared::defineBox(LInstructionHelper *lir, M return add(lir); } -template bool -LIRGeneratorShared::defineReturn(LInstructionHelper *lir, MDefinition *mir) +bool +LIRGeneratorShared::defineReturn(LInstruction *lir, MDefinition *mir) { lir->setMir(mir); @@ -316,6 +316,12 @@ LIRGeneratorShared::useFixed(MDefinition *mir, FloatRegister reg) return use(mir, LUse(reg)); } +LUse +LIRGeneratorShared::useFixed(MDefinition *mir, AnyRegister reg) +{ + return reg.isFloat() ? use(mir, reg.fpu()) : use(mir, reg.gpr()); +} + LDefinition LIRGeneratorShared::temp(LDefinition::Type type, LDefinition::Policy policy) { diff --git a/js/src/ion/shared/Lowering-shared.h b/js/src/ion/shared/Lowering-shared.h index 2947a5016c5..d1439fc9178 100644 --- a/js/src/ion/shared/Lowering-shared.h +++ b/js/src/ion/shared/Lowering-shared.h @@ -73,6 +73,7 @@ class LIRGeneratorShared : public MInstructionVisitorWithDefaults inline LUse useRegisterAtStart(MDefinition *mir); inline LUse useFixed(MDefinition *mir, Register reg); inline LUse useFixed(MDefinition *mir, FloatRegister reg); + inline LUse useFixed(MDefinition *mir, AnyRegister reg); inline LAllocation useOrConstant(MDefinition *mir); // "Any" is architecture dependent, and will include registers and stack slots on X86, // and only registers on ARM. @@ -112,8 +113,7 @@ class LIRGeneratorShared : public MInstructionVisitorWithDefaults inline bool defineBox(LInstructionHelper *lir, MDefinition *mir, LDefinition::Policy policy = LDefinition::DEFAULT); - template - inline bool defineReturn(LInstructionHelper *lir, MDefinition *mir); + inline bool defineReturn(LInstruction *lir, MDefinition *mir); template inline bool define(LInstructionHelper<1, Ops, Temps> *lir, MDefinition *mir, diff --git a/js/src/ion/shared/Lowering-x86-shared.cpp b/js/src/ion/shared/Lowering-x86-shared.cpp index 1568175683f..27e63dc4bf4 100644 --- a/js/src/ion/shared/Lowering-x86-shared.cpp +++ b/js/src/ion/shared/Lowering-x86-shared.cpp @@ -105,6 +105,34 @@ LIRGeneratorX86Shared::lowerModI(MMod *mod) return defineFixed(lir, mod, LAllocation(AnyRegister(edx))); } +bool +LIRGeneratorX86Shared::visitAsmJSNeg(MAsmJSNeg *ins) +{ + if (ins->type() == MIRType_Int32) + return defineReuseInput(new LNegI(useRegisterAtStart(ins->input())), ins, 0); + + JS_ASSERT(ins->type() == MIRType_Double); + return defineReuseInput(new LNegD(useRegisterAtStart(ins->input())), ins, 0); +} + +bool +LIRGeneratorX86Shared::visitAsmJSUDiv(MAsmJSUDiv *div) +{ + LAsmJSDivOrMod *lir = new LAsmJSDivOrMod(useFixed(div->lhs(), eax), + useRegister(div->rhs()), + tempFixed(edx)); + return defineFixed(lir, div, LAllocation(AnyRegister(eax))); +} + +bool +LIRGeneratorX86Shared::visitAsmJSUMod(MAsmJSUMod *mod) +{ + LAsmJSDivOrMod *lir = new LAsmJSDivOrMod(useFixed(mod->lhs(), eax), + useRegister(mod->rhs()), + tempFixed(edx)); + return defineFixed(lir, mod, LAllocation(AnyRegister(edx))); +} + bool LIRGeneratorX86Shared::lowerUrshD(MUrsh *mir) { diff --git a/js/src/ion/shared/Lowering-x86-shared.h b/js/src/ion/shared/Lowering-x86-shared.h index 35e71137c60..8d8f316c958 100644 --- a/js/src/ion/shared/Lowering-x86-shared.h +++ b/js/src/ion/shared/Lowering-x86-shared.h @@ -29,6 +29,9 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared bool visitGuardShape(MGuardShape *ins); bool visitPowHalf(MPowHalf *ins); bool visitConstant(MConstant *ins); + bool visitAsmJSNeg(MAsmJSNeg *ins); + bool visitAsmJSUDiv(MAsmJSUDiv *ins); + bool visitAsmJSUMod(MAsmJSUMod *ins); bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs); bool lowerDivI(MDiv *div); bool lowerModI(MMod *mod); diff --git a/js/src/ion/shared/MacroAssembler-x86-shared.h b/js/src/ion/shared/MacroAssembler-x86-shared.h index 143be05f41e..7036e05e568 100644 --- a/js/src/ion/shared/MacroAssembler-x86-shared.h +++ b/js/src/ion/shared/MacroAssembler-x86-shared.h @@ -72,6 +72,9 @@ class MacroAssemblerX86Shared : public Assembler else movl(imm, dest); } + void move32(const Imm32 &imm, const Operand &dest) { + movl(imm, dest); + } void and32(const Imm32 &imm, const Register &dest) { andl(imm, dest); } @@ -236,6 +239,9 @@ class MacroAssemblerX86Shared : public Assembler void load32(const BaseIndex &src, Register dest) { movl(Operand(src), dest); } + void load32(const Operand &src, Register dest) { + movl(src, dest); + } template void store32(const S &src, const T &dest) { movl(src, Operand(dest)); @@ -246,12 +252,18 @@ class MacroAssemblerX86Shared : public Assembler void loadDouble(const BaseIndex &src, FloatRegister dest) { movsd(Operand(src), dest); } + void loadDouble(const Operand &src, FloatRegister dest) { + movsd(src, dest); + } void storeDouble(FloatRegister src, const Address &dest) { movsd(src, Operand(dest)); } void storeDouble(FloatRegister src, const BaseIndex &dest) { movsd(src, Operand(dest)); } + void storeDouble(FloatRegister src, const Operand &dest) { + movsd(src, dest); + } void zeroDouble(FloatRegister reg) { xorpd(reg, reg); } @@ -281,6 +293,10 @@ class MacroAssemblerX86Shared : public Assembler movss(Operand(src), dest); cvtss2sd(dest, dest); } + void loadFloatAsDouble(const Operand &src, FloatRegister dest) { + movss(src, dest); + cvtss2sd(dest, dest); + } void storeFloat(FloatRegister src, const Address &dest) { movss(src, Operand(dest)); } diff --git a/js/src/ion/x64/Assembler-x64.cpp b/js/src/ion/x64/Assembler-x64.cpp index a922d53b06e..9984777b717 100644 --- a/js/src/ion/x64/Assembler-x64.cpp +++ b/js/src/ion/x64/Assembler-x64.cpp @@ -7,12 +7,80 @@ #include "Assembler-x64.h" #include "gc/Marking.h" +#include "ion/LIR.h" #include "jsscriptinlines.h" using namespace js; using namespace js::ion; +ABIArgGenerator::ABIArgGenerator() + : +#if defined(XP_WIN) + regIndex_(0), + stackOffset_(ShadowStackSpace), +#else + intRegIndex_(0), + floatRegIndex_(0), + stackOffset_(0), +#endif + current_() +{} + +ABIArg +ABIArgGenerator::next(MIRType type) +{ +#if defined(XP_WIN) + JS_STATIC_ASSERT(NumIntArgRegs == NumFloatArgRegs); + if (regIndex_ == NumIntArgRegs) { + current_ = ABIArg(stackOffset_); + stackOffset_ += sizeof(uint64_t); + return current_; + } + switch (type) { + case MIRType_Int32: + case MIRType_Pointer: + current_ = ABIArg(IntArgRegs[regIndex_++]); + break; + case MIRType_Double: + current_ = ABIArg(FloatArgRegs[regIndex_++]); + break; + default: + JS_NOT_REACHED("Unexpected argument type"); + } + return current_; +#elif defined(XP_MACOSX) || defined(__linux__) + switch (type) { + case MIRType_Int32: + case MIRType_Pointer: + if (intRegIndex_ == NumIntArgRegs) { + current_ = ABIArg(stackOffset_); + stackOffset_ += sizeof(uint64_t); + break; + } + current_ = ABIArg(IntArgRegs[intRegIndex_++]); + break; + case MIRType_Double: + if (floatRegIndex_ == NumFloatArgRegs) { + current_ = ABIArg(stackOffset_); + stackOffset_ += sizeof(uint64_t); + break; + } + current_ = ABIArg(FloatArgRegs[floatRegIndex_++]); + break; + default: + JS_NOT_REACHED("Unexpected argument type"); + } + return current_; +#else +# error "Missing ABI" +#endif +} + +const Register ABIArgGenerator::NonArgReturnVolatileReg1 = r10; +const Register ABIArgGenerator::NonArgReturnVolatileReg2 = r11; +const Register ABIArgGenerator::NonVolatileReg = r12; + void Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc) { diff --git a/js/src/ion/x64/Assembler-x64.h b/js/src/ion/x64/Assembler-x64.h index af24d9755ab..a2e483df2cc 100644 --- a/js/src/ion/x64/Assembler-x64.h +++ b/js/src/ion/x64/Assembler-x64.h @@ -72,6 +72,7 @@ static const Register JSReturnReg_Data = JSReturnReg; static const Register ReturnReg = rax; static const Register ScratchReg = r11; +static const Register HeapReg = r15; static const FloatRegister ReturnFloatReg = xmm0; static const FloatRegister ScratchFloatReg = xmm15; @@ -128,6 +129,29 @@ static const uint32_t NumFloatArgRegs = 8; static const FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 }; #endif +class ABIArgGenerator +{ +#if defined(XP_WIN) + unsigned regIndex_; +#else + unsigned intRegIndex_; + unsigned floatRegIndex_; +#endif + uint32_t stackOffset_; + ABIArg current_; + + public: + ABIArgGenerator(); + ABIArg next(MIRType argType); + ABIArg ¤t() { return current_; } + uint32_t stackBytesConsumedSoFar() const { return stackOffset_; } + + // Note: these registers are all guaranteed to be different + static const Register NonArgReturnVolatileReg1; + static const Register NonArgReturnVolatileReg2; + static const Register NonVolatileReg; +}; + static const Register OsrFrameReg = IntArgReg3; static const Register PreBarrierReg = rdx; @@ -136,6 +160,8 @@ static const Register PreBarrierReg = rdx; // jitted code. static const uint32_t StackAlignment = 16; static const bool StackKeptAligned = false; +static const uint32_t NativeFrameSize = sizeof(void*); +static const uint32_t AlignmentAtPrologue = sizeof(void*); static const Scale ScalePointer = TimesEight; @@ -365,6 +391,21 @@ class Assembler : public AssemblerX86Shared JS_NOT_REACHED("unexpected operand kind"); } } + void movq(Imm32 imm32, const Operand &dest) { + switch (dest.kind()) { + case Operand::REG: + masm.movl_i32r(imm32.value, dest.reg()); + break; + case Operand::REG_DISP: + masm.movq_i32m(imm32.value, dest.disp(), dest.base()); + break; + case Operand::SCALE: + masm.movq_i32m(imm32.value, dest.disp(), dest.base(), dest.index(), dest.scale()); + break; + default: + JS_NOT_REACHED("unexpected operand kind"); + } + } void movqsd(const Register &src, const FloatRegister &dest) { masm.movq_rr(src.code(), dest.code()); } @@ -474,6 +515,9 @@ class Assembler : public AssemblerX86Shared void mov(const Register &src, const Operand &dest) { movq(src, dest); } + void mov(const Imm32 &imm32, const Operand &dest) { + movq(imm32, dest); + } void mov(const Register &src, const Register &dest) { movq(src, dest); } @@ -497,6 +541,25 @@ class Assembler : public AssemblerX86Shared } } + CodeOffsetLabel loadRipRelativeInt32(const Register &dest) { + return CodeOffsetLabel(masm.movl_ripr(dest.code()).offset()); + } + CodeOffsetLabel loadRipRelativeInt64(const Register &dest) { + return CodeOffsetLabel(masm.movq_ripr(dest.code()).offset()); + } + CodeOffsetLabel loadRipRelativeDouble(const FloatRegister &dest) { + return CodeOffsetLabel(masm.movsd_ripr(dest.code()).offset()); + } + CodeOffsetLabel storeRipRelativeInt32(const Register &dest) { + return CodeOffsetLabel(masm.movl_rrip(dest.code()).offset()); + } + CodeOffsetLabel storeRipRelativeDouble(const FloatRegister &dest) { + return CodeOffsetLabel(masm.movsd_rrip(dest.code()).offset()); + } + CodeOffsetLabel leaRipRelative(const Register &dest) { + return CodeOffsetLabel(masm.leaq_rip(dest.code()).offset()); + } + // The below cmpq methods switch the lhs and rhs when it invokes the // macroassembler to conform with intel standard. When calling this // function put the left operand on the left as you would expect. diff --git a/js/src/ion/x64/CodeGenerator-x64.cpp b/js/src/ion/x64/CodeGenerator-x64.cpp index e56c5330ca6..ce99e7348b3 100644 --- a/js/src/ion/x64/CodeGenerator-x64.cpp +++ b/js/src/ion/x64/CodeGenerator-x64.cpp @@ -18,8 +18,8 @@ using namespace js; using namespace js::ion; -CodeGeneratorX64::CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph) - : CodeGeneratorX86Shared(gen, graph) +CodeGeneratorX64::CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) + : CodeGeneratorX86Shared(gen, graph, masm) { } @@ -339,7 +339,7 @@ CodeGeneratorX64::visitCompareB(LCompareB *lir) // Perform the comparison. masm.cmpq(lhs.valueReg(), ScratchReg); - masm.emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output); return true; } @@ -361,7 +361,7 @@ CodeGeneratorX64::visitCompareBAndBranch(LCompareBAndBranch *lir) // Perform the comparison. masm.cmpq(lhs.valueReg(), ScratchReg); - emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse()); + emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse()); return true; } bool @@ -375,7 +375,7 @@ CodeGeneratorX64::visitCompareV(LCompareV *lir) JS_ASSERT(IsEqualityOp(mir->jsop())); masm.cmpq(lhs.valueReg(), rhs.valueReg()); - masm.emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output); return true; } @@ -391,6 +391,145 @@ CodeGeneratorX64::visitCompareVAndBranch(LCompareVAndBranch *lir) mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE); masm.cmpq(lhs.valueReg(), rhs.valueReg()); - emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse()); + emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse()); return true; } + +bool +CodeGeneratorX64::visitUInt32ToDouble(LUInt32ToDouble *lir) +{ + masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + +bool +CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) +{ + MAsmJSLoadHeap *mir = ins->mir(); + ArrayBufferView::ViewType vt = mir->viewType(); + + Operand srcAddr(HeapReg, ToRegister(ins->ptr()), TimesOne); + + if (vt == ArrayBufferView::TYPE_FLOAT32) { + FloatRegister dest = ToFloatRegister(ins->output()); + uint32_t before = masm.size(); + masm.movss(srcAddr, dest); + uint32_t after = masm.size(); + masm.cvtss2sd(dest, dest); + return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output()))); + } + + uint32_t before = masm.size(); + switch (vt) { + case ArrayBufferView::TYPE_INT8: masm.movxbl(srcAddr, ToRegister(ins->output())); break; + case ArrayBufferView::TYPE_UINT8: masm.movzbl(srcAddr, ToRegister(ins->output())); break; + case ArrayBufferView::TYPE_INT16: masm.movxwl(srcAddr, ToRegister(ins->output())); break; + case ArrayBufferView::TYPE_UINT16: masm.movzwl(srcAddr, ToRegister(ins->output())); break; + case ArrayBufferView::TYPE_INT32: masm.movl(srcAddr, ToRegister(ins->output())); break; + case ArrayBufferView::TYPE_UINT32: masm.movl(srcAddr, ToRegister(ins->output())); break; + case ArrayBufferView::TYPE_FLOAT64: masm.movsd(srcAddr, ToFloatRegister(ins->output())); break; + default: JS_NOT_REACHED("unexpected array type"); + } + uint32_t after = masm.size(); + return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output()))); +} + +bool +CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) +{ + MAsmJSStoreHeap *mir = ins->mir(); + ArrayBufferView::ViewType vt = mir->viewType(); + + Operand dstAddr(HeapReg, ToRegister(ins->ptr()), TimesOne); + + if (vt == ArrayBufferView::TYPE_FLOAT32) { + masm.convertDoubleToFloat(ToFloatRegister(ins->value()), ScratchFloatReg); + uint32_t before = masm.size(); + masm.movss(ScratchFloatReg, dstAddr); + uint32_t after = masm.size(); + return gen->noteHeapAccess(AsmJSHeapAccess(before, after)); + } + + uint32_t before = masm.size(); + if (ins->value()->isConstant()) { + switch (vt) { + case ArrayBufferView::TYPE_INT8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break; + case ArrayBufferView::TYPE_UINT8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break; + case ArrayBufferView::TYPE_INT16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break; + case ArrayBufferView::TYPE_UINT16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break; + case ArrayBufferView::TYPE_INT32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break; + case ArrayBufferView::TYPE_UINT32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break; + default: JS_NOT_REACHED("unexpected array type"); + } + } else { + switch (vt) { + case ArrayBufferView::TYPE_INT8: masm.movb(ToRegister(ins->value()), dstAddr); break; + case ArrayBufferView::TYPE_UINT8: masm.movb(ToRegister(ins->value()), dstAddr); break; + case ArrayBufferView::TYPE_INT16: masm.movw(ToRegister(ins->value()), dstAddr); break; + case ArrayBufferView::TYPE_UINT16: masm.movw(ToRegister(ins->value()), dstAddr); break; + case ArrayBufferView::TYPE_INT32: masm.movl(ToRegister(ins->value()), dstAddr); break; + case ArrayBufferView::TYPE_UINT32: masm.movl(ToRegister(ins->value()), dstAddr); break; + case ArrayBufferView::TYPE_FLOAT64: masm.movsd(ToFloatRegister(ins->value()), dstAddr); break; + default: JS_NOT_REACHED("unexpected array type"); + } + } + uint32_t after = masm.size(); + return gen->noteHeapAccess(AsmJSHeapAccess(before, after)); +} + +bool +CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins) +{ + MAsmJSLoadGlobalVar *mir = ins->mir(); + + CodeOffsetLabel label; + if (mir->type() == MIRType_Int32) + label = masm.loadRipRelativeInt32(ToRegister(ins->output())); + else + label = masm.loadRipRelativeDouble(ToFloatRegister(ins->output())); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + +bool +CodeGeneratorX64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) +{ + MAsmJSStoreGlobalVar *mir = ins->mir(); + + MIRType type = mir->value()->type(); + JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + + CodeOffsetLabel label; + if (type == MIRType_Int32) + label = masm.storeRipRelativeInt32(ToRegister(ins->value())); + else + label = masm.storeRipRelativeDouble(ToFloatRegister(ins->value())); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + +bool +CodeGeneratorX64::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins) +{ + MAsmJSLoadFuncPtr *mir = ins->mir(); + + Register index = ToRegister(ins->index()); + Register tmp = ToRegister(ins->temp()); + Register out = ToRegister(ins->output()); + + CodeOffsetLabel label = masm.leaRipRelative(tmp); + masm.loadPtr(Operand(tmp, index, TimesEight, 0), out); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + +bool +CodeGeneratorX64::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins) +{ + MAsmJSLoadFFIFunc *mir = ins->mir(); + + CodeOffsetLabel label = masm.loadRipRelativeInt64(ToRegister(ins->output())); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + diff --git a/js/src/ion/x64/CodeGenerator-x64.h b/js/src/ion/x64/CodeGenerator-x64.h index 9e1b06b1546..eb897bce6cc 100644 --- a/js/src/ion/x64/CodeGenerator-x64.h +++ b/js/src/ion/x64/CodeGenerator-x64.h @@ -34,7 +34,7 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared const Register &elements, const LAllocation *index); public: - CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph); + CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); public: bool visitValue(LValue *value); @@ -52,6 +52,15 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared bool visitCompareBAndBranch(LCompareBAndBranch *lir); bool visitCompareV(LCompareV *lir); bool visitCompareVAndBranch(LCompareVAndBranch *lir); + bool visitUInt32ToDouble(LUInt32ToDouble *lir); + bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins); + bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins); + bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins); + bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins); + bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins); + bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins); + + void postAsmJSCall(LAsmJSCall *lir) {} }; typedef CodeGeneratorX64 CodeGeneratorSpecific; diff --git a/js/src/ion/x64/LIR-x64.h b/js/src/ion/x64/LIR-x64.h index 6b3c4cf0478..f1a8d03fe3a 100644 --- a/js/src/ion/x64/LIR-x64.h +++ b/js/src/ion/x64/LIR-x64.h @@ -66,6 +66,36 @@ class LUnboxDouble : public LUnboxBase { { } }; +// Convert a 32-bit unsigned integer to a double. +class LUInt32ToDouble : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(UInt32ToDouble) + + LUInt32ToDouble(const LAllocation &input) { + setOperand(0, input); + } +}; + +class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(AsmJSLoadFuncPtr); + LAsmJSLoadFuncPtr(const LAllocation &index, const LDefinition &temp) { + setOperand(0, index); + setTemp(0, temp); + } + MAsmJSLoadFuncPtr *mir() const { + return mir_->toAsmJSLoadFuncPtr(); + } + const LAllocation *index() { + return getOperand(0); + } + const LDefinition *temp() { + return getTemp(0); + } +}; + } // namespace ion } // namespace js diff --git a/js/src/ion/x64/LOpcodes-x64.h b/js/src/ion/x64/LOpcodes-x64.h index 157d0a1a51e..8a276914ac2 100644 --- a/js/src/ion/x64/LOpcodes-x64.h +++ b/js/src/ion/x64/LOpcodes-x64.h @@ -15,7 +15,10 @@ _(DivI) \ _(ModI) \ _(ModPowTwoI) \ - _(PowHalfD) + _(PowHalfD) \ + _(UInt32ToDouble) \ + _(AsmJSLoadFuncPtr) \ + _(AsmJSDivOrMod) #endif // jsion_lir_opcodes_x64_h__ diff --git a/js/src/ion/x64/Lowering-x64.cpp b/js/src/ion/x64/Lowering-x64.cpp index 282df031f90..a22f219f072 100644 --- a/js/src/ion/x64/Lowering-x64.cpp +++ b/js/src/ion/x64/Lowering-x64.cpp @@ -145,3 +145,40 @@ LIRGeneratorX64::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins) LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); return add(new LStoreTypedArrayElement(elements, index, value), ins); } + +bool +LIRGeneratorX64::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins) +{ + JS_ASSERT(ins->input()->type() == MIRType_Int32); + LUInt32ToDouble *lir = new LUInt32ToDouble(useRegisterAtStart(ins->input())); + return define(lir, ins); +} + +bool +LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) +{ + LAsmJSStoreHeap *lir; + switch (ins->viewType()) { + case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8: + case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16: + case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: + lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()), + useRegisterOrConstantAtStart(ins->value())); + break; + case ArrayBufferView::TYPE_FLOAT32: + case ArrayBufferView::TYPE_FLOAT64: + lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()), + useRegisterAtStart(ins->value())); + break; + default: JS_NOT_REACHED("unexpected array type"); + } + + return add(lir, ins); +} + +bool +LIRGeneratorX64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) +{ + return define(new LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins); +} + diff --git a/js/src/ion/x64/Lowering-x64.h b/js/src/ion/x64/Lowering-x64.h index de6490ad3c0..63ac7399bb5 100644 --- a/js/src/ion/x64/Lowering-x64.h +++ b/js/src/ion/x64/Lowering-x64.h @@ -42,6 +42,9 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared bool visitUnbox(MUnbox *unbox); bool visitReturn(MReturn *ret); bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins); + bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins); + bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins); + bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins); }; typedef LIRGeneratorX64 LIRGeneratorSpecific; diff --git a/js/src/ion/x64/MacroAssembler-x64.h b/js/src/ion/x64/MacroAssembler-x64.h index f4d67de86d3..8b422524bf3 100644 --- a/js/src/ion/x64/MacroAssembler-x64.h +++ b/js/src/ion/x64/MacroAssembler-x64.h @@ -451,6 +451,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared testq(lhs, rhs); j(cond, label); } + void branchTestPtr(Condition cond, Register lhs, Imm32 imm, Label *label) { + testq(lhs, imm); + j(cond, label); + } void decBranchPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) { subPtr(imm, lhs); j(cond, label); @@ -459,6 +463,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared void movePtr(const Register &src, const Register &dest) { movq(src, dest); } + void movePtr(const Register &src, const Operand &dest) { + movq(src, dest); + } void movePtr(ImmWord imm, Register dest) { movq(imm, dest); } @@ -472,6 +479,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared void loadPtr(const Address &address, Register dest) { movq(Operand(address), dest); } + void loadPtr(const Operand &src, Register dest) { + movq(src, dest); + } void loadPtr(const BaseIndex &src, Register dest) { movq(Operand(src), dest); } @@ -490,6 +500,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared void storePtr(Register src, const Address &address) { movq(src, Operand(address)); } + void storePtr(Register src, const Operand &dest) { + movq(src, dest); + } void storePtr(const Register &src, const AbsoluteAddress &address) { movq(ImmWord(address.addr), ScratchReg); movq(src, Operand(ScratchReg, 0x0)); @@ -709,6 +722,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared lea(src, ScratchReg); movqsd(ScratchReg, dest); } + void unboxDouble(const Address &src, const FloatRegister &dest) { + unboxDouble(Operand(src), dest); + } void unboxPrivate(const ValueOperand &src, const Register dest) { movq(src.valueReg(), dest); shlq(Imm32(1), dest); @@ -915,6 +931,16 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared call(code); addq(Imm32(sizeof(uintptr_t) * 2), rsp); } + + // See CodeGeneratorX64 calls to noteAsmJSGlobalAccess. + void patchAsmJSGlobalAccess(unsigned offset, uint8_t *code, unsigned codeBytes, + unsigned globalDataOffset) + { + uint8_t *nextInsn = code + offset; + JS_ASSERT(nextInsn <= code + codeBytes); + uint8_t *target = code + codeBytes + globalDataOffset; + ((int32_t *)nextInsn)[-1] = target - nextInsn; + } }; typedef MacroAssemblerX64 MacroAssemblerSpecific; diff --git a/js/src/ion/x86/Architecture-x86.h b/js/src/ion/x86/Architecture-x86.h index 7503f278f49..db1b829a14b 100644 --- a/js/src/ion/x86/Architecture-x86.h +++ b/js/src/ion/x86/Architecture-x86.h @@ -21,6 +21,9 @@ static const uint32_t DOUBLE_STACK_ALIGNMENT = 2; // +8 for double spills static const uint32_t ION_FRAME_SLACK_SIZE = 20; +// Only Win64 requires shadow stack space. +static const uint32_t ShadowStackSpace = 0; + // An offset that is illegal for a local variable's stack allocation. static const int32_t INVALID_STACK_SLOT = -1; diff --git a/js/src/ion/x86/Assembler-x86.cpp b/js/src/ion/x86/Assembler-x86.cpp index 2f5cf4c3dc7..d9cf3d312c9 100644 --- a/js/src/ion/x86/Assembler-x86.cpp +++ b/js/src/ion/x86/Assembler-x86.cpp @@ -11,6 +11,33 @@ using namespace js; using namespace js::ion; +ABIArgGenerator::ABIArgGenerator() + : stackOffset_(0), + current_() +{} + +ABIArg +ABIArgGenerator::next(MIRType type) +{ + current_ = ABIArg(stackOffset_); + switch (type) { + case MIRType_Int32: + case MIRType_Pointer: + stackOffset_ += sizeof(uint32_t); + break; + case MIRType_Double: + stackOffset_ += sizeof(uint64_t); + break; + default: + JS_NOT_REACHED("Unexpected argument type"); + } + return current_; +} + +const Register ABIArgGenerator::NonArgReturnVolatileReg1 = ecx; +const Register ABIArgGenerator::NonArgReturnVolatileReg2 = edx; +const Register ABIArgGenerator::NonVolatileReg = ebx; + void Assembler::executableCopy(uint8_t *buffer) { diff --git a/js/src/ion/x86/Assembler-x86.h b/js/src/ion/x86/Assembler-x86.h index 48f9fade06a..927d9d252d7 100644 --- a/js/src/ion/x86/Assembler-x86.h +++ b/js/src/ion/x86/Assembler-x86.h @@ -61,6 +61,23 @@ static const Register CallTempNonArgRegs[] = { edi, eax, ebx, ecx, esi, edx }; static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); +class ABIArgGenerator +{ + uint32_t stackOffset_; + ABIArg current_; + + public: + ABIArgGenerator(); + ABIArg next(MIRType argType); + ABIArg ¤t() { return current_; } + uint32_t stackBytesConsumedSoFar() const { return stackOffset_; } + + // Note: these registers are all guaranteed to be different + static const Register NonArgReturnVolatileReg1; + static const Register NonArgReturnVolatileReg2; + static const Register NonVolatileReg; +}; + static const Register OsrFrameReg = edx; static const Register PreBarrierReg = edx; @@ -72,6 +89,8 @@ static const uint32_t StackAlignment = 16; static const uint32_t StackAlignment = 4; #endif static const bool StackKeptAligned = false; +static const uint32_t NativeFrameSize = sizeof(void*); +static const uint32_t AlignmentAtPrologue = sizeof(void*); struct ImmTag : public Imm32 { @@ -299,6 +318,9 @@ class Assembler : public AssemblerX86Shared void mov(const Register &src, const Operand &dest) { movl(src, dest); } + void mov(Imm32 imm, const Operand &dest) { + movl(imm, dest); + } void mov(AbsoluteLabel *label, const Register &dest) { JS_ASSERT(!label->bound()); // Thread the patch list through the unpatched address word in the @@ -310,16 +332,7 @@ class Assembler : public AssemblerX86Shared movl(src, dest); } void lea(const Operand &src, const Register &dest) { - switch (src.kind()) { - case Operand::REG_DISP: - masm.leal_mr(src.disp(), src.base(), dest.code()); - break; - case Operand::SCALE: - masm.leal_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); - break; - default: - JS_NOT_REACHED("unexpected operand kind"); - } + return leal(src, dest); } void cmpl(const Register src, ImmWord ptr) { @@ -350,6 +363,10 @@ class Assembler : public AssemblerX86Shared JS_NOT_REACHED("unexpected operand kind"); } } + CodeOffsetLabel cmplWithPatch(const Register &lhs, Imm32 rhs) { + masm.cmpl_ir_force32(rhs.value, lhs.code()); + return masm.currentOffset(); + } void jmp(void *target, Relocation::Kind reloc = Relocation::HARDCODED) { JmpSrc src = masm.jmp(); @@ -405,6 +422,91 @@ class Assembler : public AssemblerX86Shared void movsd(const double *dp, const FloatRegister &dest) { masm.movsd_mr((const void *)dp, dest.code()); } + + // Move a 32-bit immediate into a register where the immediate can be + // patched. + CodeOffsetLabel movlWithPatch(Imm32 imm, Register dest) { + masm.movl_i32r(imm.value, dest.code()); + return masm.currentOffset(); + } + + // Load from *addr where addr can be patched. + CodeOffsetLabel movlWithPatch(void *addr, Register dest) { + masm.movl_mr(addr, dest.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movsdWithPatch(void *addr, FloatRegister dest) { + masm.movsd_mr(addr, dest.code()); + return masm.currentOffset(); + } + + // Store to *addr where addr can be patched + CodeOffsetLabel movlWithPatch(Register src, void *addr) { + masm.movl_rm(src.code(), addr); + return masm.currentOffset(); + } + CodeOffsetLabel movsdWithPatch(FloatRegister dest, void *addr) { + masm.movsd_rm(dest.code(), addr); + return masm.currentOffset(); + } + + // Load from *(base + disp32) where disp32 can be patched. + CodeOffsetLabel movxblWithPatch(Address src, Register dest) { + masm.movxbl_mr_disp32(src.offset, src.base.code(), dest.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movzblWithPatch(Address src, Register dest) { + masm.movzbl_mr_disp32(src.offset, src.base.code(), dest.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movxwlWithPatch(Address src, Register dest) { + masm.movxwl_mr_disp32(src.offset, src.base.code(), dest.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movzwlWithPatch(Address src, Register dest) { + masm.movzwl_mr_disp32(src.offset, src.base.code(), dest.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movlWithPatch(Address src, Register dest) { + masm.movl_mr_disp32(src.offset, src.base.code(), dest.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movssWithPatch(Address src, FloatRegister dest) { + masm.movss_mr_disp32(src.offset, src.base.code(), dest.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movsdWithPatch(Address src, FloatRegister dest) { + masm.movsd_mr_disp32(src.offset, src.base.code(), dest.code()); + return masm.currentOffset(); + } + + // Store to *(base + disp32) where disp32 can be patched. + CodeOffsetLabel movbWithPatch(Register src, Address dest) { + masm.movb_rm_disp32(src.code(), dest.offset, dest.base.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movwWithPatch(Register src, Address dest) { + masm.movw_rm_disp32(src.code(), dest.offset, dest.base.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movlWithPatch(Register src, Address dest) { + masm.movl_rm_disp32(src.code(), dest.offset, dest.base.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movssWithPatch(FloatRegister src, Address dest) { + masm.movss_rm_disp32(src.code(), dest.offset, dest.base.code()); + return masm.currentOffset(); + } + CodeOffsetLabel movsdWithPatch(FloatRegister src, Address dest) { + masm.movsd_rm_disp32(src.code(), dest.offset, dest.base.code()); + return masm.currentOffset(); + } + + // Load from *(addr + index*scale) where addr can be patched. + CodeOffsetLabel movlWithPatch(void *addr, Register index, Scale scale, Register dest) { + masm.movl_mr(addr, index.code(), scale, dest.code()); + return masm.currentOffset(); + } }; // Get a register in which we plan to put a quantity that will be used as an diff --git a/js/src/ion/x86/CodeGenerator-x86.cpp b/js/src/ion/x86/CodeGenerator-x86.cpp index ae2411d788a..34e7574f540 100644 --- a/js/src/ion/x86/CodeGenerator-x86.cpp +++ b/js/src/ion/x86/CodeGenerator-x86.cpp @@ -23,8 +23,8 @@ using namespace js::ion; using mozilla::DebugOnly; -CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph) - : CodeGeneratorX86Shared(gen, graph) +CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) + : CodeGeneratorX86Shared(gen, graph, masm) { } @@ -312,7 +312,7 @@ CodeGeneratorX86::visitCompareB(LCompareB *lir) masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); else masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); - masm.emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output); masm.jump(&done); } masm.bind(¬Boolean); @@ -342,7 +342,7 @@ CodeGeneratorX86::visitCompareBAndBranch(LCompareBAndBranch *lir) masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); else masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); - emitBranch(JSOpToCondition(mir->jsop()), lir->ifTrue(), lir->ifFalse()); + emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse()); return true; } @@ -350,7 +350,7 @@ bool CodeGeneratorX86::visitCompareV(LCompareV *lir) { MCompare *mir = lir->mir(); - Assembler::Condition cond = JSOpToCondition(mir->jsop()); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput); const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput); const Register output = ToRegister(lir->output()); @@ -378,7 +378,7 @@ bool CodeGeneratorX86::visitCompareVAndBranch(LCompareVAndBranch *lir) { MCompare *mir = lir->mir(); - Assembler::Condition cond = JSOpToCondition(mir->jsop()); + Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput); const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput); @@ -398,3 +398,184 @@ CodeGeneratorX86::visitCompareVAndBranch(LCompareVAndBranch *lir) return true; } + +bool +CodeGeneratorX86::visitUInt32ToDouble(LUInt32ToDouble *lir) +{ + Register input = ToRegister(lir->input()); + Register temp = ToRegister(lir->temp()); + + if (input != temp) + masm.mov(input, temp); + + // Beware: convertUInt32ToDouble clobbers input. + masm.convertUInt32ToDouble(temp, ToFloatRegister(lir->output())); + return true; +} + +class ion::OutOfLineAsmJSLoadHeapOutOfBounds : public OutOfLineCodeBase +{ + AnyRegister dest_; + public: + OutOfLineAsmJSLoadHeapOutOfBounds(AnyRegister dest) : dest_(dest) {} + const AnyRegister &dest() const { return dest_; } + bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineAsmJSLoadHeapOutOfBounds(this); } +}; + +bool +CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) +{ + const MAsmJSLoadHeap *mir = ins->mir(); + ArrayBufferView::ViewType vt = mir->viewType(); + + Register ptr = ToRegister(ins->ptr()); + const LDefinition *out = ins->output(); + + OutOfLineAsmJSLoadHeapOutOfBounds *ool = new OutOfLineAsmJSLoadHeapOutOfBounds(ToAnyRegister(out)); + if (!addOutOfLineCode(ool)) + return false; + + CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0)); + masm.j(Assembler::AboveOrEqual, ool->entry()); + + Address srcAddr(ptr, 0); + if (vt == ArrayBufferView::TYPE_FLOAT32) { + FloatRegister dest = ToFloatRegister(out); + uint32_t before = masm.size(); + masm.movssWithPatch(srcAddr, dest); + uint32_t after = masm.size(); + masm.cvtss2sd(dest, dest); + masm.bind(ool->rejoin()); + return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after, vt, AnyRegister(dest))); + } + uint32_t before = masm.size(); + switch (vt) { + case ArrayBufferView::TYPE_INT8: masm.movxblWithPatch(srcAddr, ToRegister(out)); break; + case ArrayBufferView::TYPE_UINT8: masm.movzblWithPatch(srcAddr, ToRegister(out)); break; + case ArrayBufferView::TYPE_INT16: masm.movxwlWithPatch(srcAddr, ToRegister(out)); break; + case ArrayBufferView::TYPE_UINT16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break; + case ArrayBufferView::TYPE_INT32: masm.movlWithPatch(srcAddr, ToRegister(out)); break; + case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(srcAddr, ToRegister(out)); break; + case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break; + default: JS_NOT_REACHED("unexpected array type"); + } + uint32_t after = masm.size(); + masm.bind(ool->rejoin()); + return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after, vt, ToAnyRegister(out))); +} + +bool +CodeGeneratorX86::visitOutOfLineAsmJSLoadHeapOutOfBounds(OutOfLineAsmJSLoadHeapOutOfBounds *ool) +{ + if (ool->dest().isFloat()) + masm.movsd(&js_NaN, ool->dest().fpu()); + else + masm.movl(Imm32(0), ool->dest().gpr()); + masm.jmp(ool->rejoin()); + return true; +} + +bool +CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) +{ + MAsmJSStoreHeap *mir = ins->mir(); + ArrayBufferView::ViewType vt = mir->viewType(); + + Register ptr = ToRegister(ins->ptr()); + const LAllocation *value = ins->value(); + + CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0)); + Label rejoin; + masm.j(Assembler::AboveOrEqual, &rejoin); + + Address dstAddr(ptr, 0); + if (vt == ArrayBufferView::TYPE_FLOAT32) { + masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg); + uint32_t before = masm.size(); + masm.movssWithPatch(ScratchFloatReg, dstAddr); + uint32_t after = masm.size(); + masm.bind(&rejoin); + return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after)); + } + uint32_t before = masm.size(); + switch (vt) { + case ArrayBufferView::TYPE_INT8: masm.movbWithPatch(ToRegister(value), dstAddr); break; + case ArrayBufferView::TYPE_UINT8: masm.movbWithPatch(ToRegister(value), dstAddr); break; + case ArrayBufferView::TYPE_INT16: masm.movwWithPatch(ToRegister(value), dstAddr); break; + case ArrayBufferView::TYPE_UINT16: masm.movwWithPatch(ToRegister(value), dstAddr); break; + case ArrayBufferView::TYPE_INT32: masm.movlWithPatch(ToRegister(value), dstAddr); break; + case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(ToRegister(value), dstAddr); break; + case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break; + default: JS_NOT_REACHED("unexpected array type"); + } + uint32_t after = masm.size(); + masm.bind(&rejoin); + return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after)); +} + +bool +CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins) +{ + MAsmJSLoadGlobalVar *mir = ins->mir(); + + CodeOffsetLabel label; + if (mir->type() == MIRType_Int32) + label = masm.movlWithPatch(NULL, ToRegister(ins->output())); + else + label = masm.movsdWithPatch(NULL, ToFloatRegister(ins->output())); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + +bool +CodeGeneratorX86::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) +{ + MAsmJSStoreGlobalVar *mir = ins->mir(); + + MIRType type = mir->value()->type(); + JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double); + + CodeOffsetLabel label; + if (type == MIRType_Int32) + label = masm.movlWithPatch(ToRegister(ins->value()), NULL); + else + label = masm.movsdWithPatch(ToFloatRegister(ins->value()), NULL); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + +bool +CodeGeneratorX86::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins) +{ + MAsmJSLoadFuncPtr *mir = ins->mir(); + + Register index = ToRegister(ins->index()); + Register out = ToRegister(ins->output()); + CodeOffsetLabel label = masm.movlWithPatch(NULL, index, TimesFour, out); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + +bool +CodeGeneratorX86::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins) +{ + MAsmJSLoadFFIFunc *mir = ins->mir(); + + Register out = ToRegister(ins->output()); + CodeOffsetLabel label = masm.movlWithPatch(NULL, out); + + return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset()); +} + +void +CodeGeneratorX86::postAsmJSCall(LAsmJSCall *lir) +{ + MAsmJSCall *mir = lir->mir(); + if (mir->type() != MIRType_Double || mir->callee().which() != MAsmJSCall::Callee::Builtin) + return; + + masm.reserveStack(sizeof(double)); + masm.fstp(Operand(esp, 0)); + masm.movsd(Operand(esp, 0), ReturnFloatReg); + masm.freeStack(sizeof(double)); +} diff --git a/js/src/ion/x86/CodeGenerator-x86.h b/js/src/ion/x86/CodeGenerator-x86.h index a408ba2358f..581e099517f 100644 --- a/js/src/ion/x86/CodeGenerator-x86.h +++ b/js/src/ion/x86/CodeGenerator-x86.h @@ -14,6 +14,8 @@ namespace js { namespace ion { +class OutOfLineAsmJSLoadHeapOutOfBounds; + class CodeGeneratorX86 : public CodeGeneratorX86Shared { private: @@ -30,7 +32,7 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared const Register &elements, const LAllocation *index); public: - CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph); + CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); public: bool visitBox(LBox *box); @@ -49,6 +51,17 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared bool visitCompareBAndBranch(LCompareBAndBranch *lir); bool visitCompareV(LCompareV *lir); bool visitCompareVAndBranch(LCompareVAndBranch *lir); + bool visitUInt32ToDouble(LUInt32ToDouble *lir); + bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins); + bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins); + bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins); + bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins); + bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins); + bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins); + + bool visitOutOfLineAsmJSLoadHeapOutOfBounds(OutOfLineAsmJSLoadHeapOutOfBounds *ool); + + void postAsmJSCall(LAsmJSCall *lir); }; typedef CodeGeneratorX86 CodeGeneratorSpecific; diff --git a/js/src/ion/x86/LIR-x86.h b/js/src/ion/x86/LIR-x86.h index 7f744623a00..b56ab478877 100644 --- a/js/src/ion/x86/LIR-x86.h +++ b/js/src/ion/x86/LIR-x86.h @@ -70,6 +70,36 @@ class LUnboxDouble : public LInstructionHelper<1, 2, 0> } }; +// Convert a 32-bit unsigned integer to a double. +class LUInt32ToDouble : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(UInt32ToDouble) + + LUInt32ToDouble(const LAllocation &input, const LDefinition &temp) { + setOperand(0, input); + setTemp(0, temp); + } + const LDefinition *temp() { + return getTemp(0); + } +}; + +class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(AsmJSLoadFuncPtr); + LAsmJSLoadFuncPtr(const LAllocation &index) { + setOperand(0, index); + } + MAsmJSLoadFuncPtr *mir() const { + return mir_->toAsmJSLoadFuncPtr(); + } + const LAllocation *index() { + return getOperand(0); + } +}; + } // namespace ion } // namespace js diff --git a/js/src/ion/x86/LOpcodes-x86.h b/js/src/ion/x86/LOpcodes-x86.h index 5bc9f5fa8ad..7944813552d 100644 --- a/js/src/ion/x86/LOpcodes-x86.h +++ b/js/src/ion/x86/LOpcodes-x86.h @@ -16,7 +16,10 @@ _(DivI) \ _(ModI) \ _(ModPowTwoI) \ - _(PowHalfD) + _(PowHalfD) \ + _(UInt32ToDouble) \ + _(AsmJSLoadFuncPtr) \ + _(AsmJSDivOrMod) #endif // jsion_lir_opcodes_x86_h__ diff --git a/js/src/ion/x86/Lowering-x86.cpp b/js/src/ion/x86/Lowering-x86.cpp index b4e8f62540c..be7d73c434d 100644 --- a/js/src/ion/x86/Lowering-x86.cpp +++ b/js/src/ion/x86/Lowering-x86.cpp @@ -219,3 +219,46 @@ LIRGeneratorX86::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins) value = useRegisterOrNonDoubleConstant(ins->value()); return add(new LStoreTypedArrayElement(elements, index, value), ins); } + +bool +LIRGeneratorX86::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins) +{ + JS_ASSERT(ins->input()->type() == MIRType_Int32); + LUInt32ToDouble *lir = new LUInt32ToDouble(useRegisterAtStart(ins->input()), temp()); + return define(lir, ins); +} + +bool +LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) +{ + LAsmJSStoreHeap *lir; + switch (ins->viewType()) { + case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8: + // It's a trap! On x86, the 1-byte store can only use one of + // {al,bl,cl,dl,ah,bh,ch,dh}. That means if the register allocator + // gives us one of {edi,esi,ebp,esp}, we're out of luck. (The formatter + // will assert on us.) Ideally, we'd just ask the register allocator to + // give us one of {al,bl,cl,dl}. For now, just useFixed(al). + lir = new LAsmJSStoreHeap(useRegister(ins->ptr()), + useFixed(ins->value(), eax)); + break; + case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16: + case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: + case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64: + // For now, don't allow constants. The immediate operand affects + // instruction layout which affects patching. + lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()), + useRegisterAtStart(ins->value())); + break; + default: JS_NOT_REACHED("unexpected array type"); + } + + return add(lir, ins); +} + +bool +LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) +{ + return define(new LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins); +} + diff --git a/js/src/ion/x86/Lowering-x86.h b/js/src/ion/x86/Lowering-x86.h index bf7d5749d4d..99883f4b8dd 100644 --- a/js/src/ion/x86/Lowering-x86.h +++ b/js/src/ion/x86/Lowering-x86.h @@ -44,6 +44,9 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared bool visitUnbox(MUnbox *unbox); bool visitReturn(MReturn *ret); bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins); + bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins); + bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins); + bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins); bool lowerPhi(MPhi *phi); static bool allowTypedElementHoleCheck() { diff --git a/js/src/ion/x86/MacroAssembler-x86.h b/js/src/ion/x86/MacroAssembler-x86.h index cde0b0302b3..6ad31de411b 100644 --- a/js/src/ion/x86/MacroAssembler-x86.h +++ b/js/src/ion/x86/MacroAssembler-x86.h @@ -209,6 +209,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void movePtr(const Register &src, const Register &dest) { movl(src, dest); } + void movePtr(const Register &src, const Operand &dest) { + movl(src, dest); + } // Returns the register containing the type tag. Register splitTagForTest(const ValueOperand &value) { @@ -461,6 +464,10 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared testl(lhs, rhs); j(cond, label); } + void branchTestPtr(Condition cond, Register lhs, Imm32 imm, Label *label) { + testl(lhs, imm); + j(cond, label); + } void decBranchPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) { subPtr(imm, lhs); j(cond, label); @@ -475,6 +482,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void loadPtr(const Address &address, Register dest) { movl(Operand(address), dest); } + void loadPtr(const Operand &src, Register dest) { + movl(src, dest); + } void loadPtr(const BaseIndex &src, Register dest) { movl(Operand(src), dest); } @@ -493,6 +503,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void storePtr(Register src, const Address &address) { movl(src, Operand(address)); } + void storePtr(Register src, const Operand &dest) { + movl(src, dest); + } void storePtr(Register src, const AbsoluteAddress &address) { movl(src, Operand(address)); } @@ -800,6 +813,16 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared call(code); addl(Imm32(sizeof(uintptr_t) * 2), esp); } + + // See CodeGeneratorX86 calls to noteAsmJSGlobalAccess. + void patchAsmJSGlobalAccess(unsigned offset, uint8_t *code, unsigned codeBytes, + unsigned globalDataOffset) + { + uint8_t *nextInsn = code + offset; + JS_ASSERT(nextInsn <= code + codeBytes); + uint8_t *target = code + codeBytes + globalDataOffset; + ((int32_t *)nextInsn)[-1] = uintptr_t(target); + } }; typedef MacroAssemblerX86 MacroAssemblerSpecific; diff --git a/js/src/jit-test/lib/asm.js b/js/src/jit-test/lib/asm.js new file mode 100644 index 00000000000..9eb30ab41e2 --- /dev/null +++ b/js/src/jit-test/lib/asm.js @@ -0,0 +1,147 @@ +/* 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/. */ + +const ASM_OK_STRING = "Successfully compiled asm.js code"; +const ASM_TYPE_FAIL_STRING = "asm.js type error:"; + +const USE_ASM = "'use asm';"; +const HEAP_IMPORTS = "var i8=new glob.Int8Array(b);var u8=new glob.Uint8Array(b);"+ + "var i16=new glob.Int16Array(b);var u16=new glob.Uint16Array(b);"+ + "var i32=new glob.Int32Array(b);var u32=new glob.Uint32Array(b);"+ + "var f32=new glob.Float32Array(b);var f64=new glob.Float64Array(b);"; +const BUF_64KB = new ArrayBuffer(64 * 1024); + +function asmCompile() +{ + if (!isAsmJSCompilationAvailable()) + return Function.apply(null, arguments); + + // asm.js emits a warning on successful compilation + + // Turn on warnings-as-errors + var oldOpts = options("werror"); + assertEq(oldOpts.indexOf("werror"), -1); + + // Verify that the code is succesfully compiled + var caught = false; + try { + Function.apply(null, arguments); + } catch (e) { + if ((''+e).indexOf(ASM_OK_STRING) == -1) + throw new Error("Didn't catch the expected success error; instead caught: " + e); + caught = true; + } + if (!caught) + throw new Error("Didn't catch the success error"); + + // Turn warnings-as-errors back off + options("werror"); + + // Compile for real + return Function.apply(null, arguments); +} + +function assertAsmTypeFail() +{ + if (!isAsmJSCompilationAvailable()) + return; + + // Verify no error is thrown with warnings off + Function.apply(null, arguments); + + // Turn on warnings-as-errors + var oldOpts = options("werror"); + assertEq(oldOpts.indexOf("werror"), -1); + + // Verify an error is thrown + var caught = false; + try { + Function.apply(null, arguments); + } catch (e) { + if ((''+e).indexOf(ASM_TYPE_FAIL_STRING) == -1) + throw new Error("Didn't catch the expected type failure error; instead caught: " + e); + caught = true; + } + if (!caught) + throw new Error("Didn't catch the type failure error"); + + // Turn warnings-as-errors back off + options("werror"); +} + +function assertAsmLinkFail(f) +{ + if (!isAsmJSCompilationAvailable()) + return; + + // Verify no error is thrown with warnings off + f.apply(null, Array.slice(arguments, 1)); + + // Turn on warnings-as-errors + var oldOpts = options("werror"); + assertEq(oldOpts.indexOf("werror"), -1); + + // Verify an error is thrown + var caught = false; + try { + f.apply(null, Array.slice(arguments, 1)); + } catch (e) { + // Arbitrary code an run in the GetProperty, so don't assert any + // particular string + caught = true; + } + if (!caught) + throw new Error("Didn't catch the link failure error"); + + // Turn warnings-as-errors back off + options("werror"); +} + +// Linking should throw an exception even without warnings-as-errors +function assertAsmLinkAlwaysFail(f) +{ + var caught = false; + try { + f.apply(null, Array.slice(arguments, 1)); + } catch (e) { + caught = true; + } + if (!caught) + throw new Error("Didn't catch the link failure error"); + + // Turn on warnings-as-errors + var oldOpts = options("werror"); + assertEq(oldOpts.indexOf("werror"), -1); + + // Verify an error is thrown + var caught = false; + try { + f.apply(null, Array.slice(arguments, 1)); + } catch (e) { + caught = true; + } + if (!caught) + throw new Error("Didn't catch the link failure error"); + + // Turn warnings-as-errors back off + options("werror"); +} + +// Linking should throw a warning-as-error but otherwise run fine +function asmLink(f) +{ + if (!isAsmJSCompilationAvailable()) + return f.apply(null, Array.slice(arguments, 1)); + + // Turn on warnings-as-errors + var oldOpts = options("werror"); + assertEq(oldOpts.indexOf("werror"), -1); + + var ret = f.apply(null, Array.slice(arguments, 1)); + + // Turn warnings-as-errors back off + options("werror"); + + return ret; +} diff --git a/js/src/jit-test/tests/asm.js/testBasic.js b/js/src/jit-test/tests/asm.js/testBasic.js new file mode 100644 index 00000000000..7d4c2f3233e --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testBasic.js @@ -0,0 +1,98 @@ +load(libdir + "asm.js"); + +assertAsmTypeFail(USE_ASM); +assertAsmTypeFail(USE_ASM + 'return'); +assertAsmTypeFail(USE_ASM + 'function f() 0'); +assertAsmTypeFail(USE_ASM + 'function f(){}'); +assertAsmTypeFail(USE_ASM + 'function f(){} return 0'); +assertAsmTypeFail(USE_ASM + 'function f() 0; return 0'); +assertAsmTypeFail(USE_ASM + 'function f(){} return g'); +assertAsmTypeFail(USE_ASM + 'function f() 0; return f'); +assertAsmTypeFail('"use strict";' + USE_ASM + 'function f() {} return f'); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(){} return f'))(), undefined); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(){;} return f'))(), undefined); +assertAsmTypeFail(USE_ASM + 'function f(i,j){;} return f'); +assertEq(asmLink(asmCompile('"use asm";; function f(){};;; return f;;'))(), undefined); +assertAsmTypeFail(USE_ASM + 'function f(x){} return f'); +assertAsmTypeFail(USE_ASM + 'function f(){return; return 1} return f'); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0} return f'))(42), undefined); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0; return x|0} return f'))(42), 42); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(x,y){x=x|0;y=y|0; return (x+y)|0} return f'))(44, -2), 42); +assertAsmTypeFail('a', USE_ASM + 'function a(){} return a'); +assertAsmTypeFail('a','b','c', USE_ASM + 'var c'); +assertAsmTypeFail('a','b','c', USE_ASM + 'var c=0'); +assertAsmTypeFail('x','x', USE_ASM + 'function a(){} return a'); +assertAsmTypeFail('x','y','x', USE_ASM + 'function a(){} return a'); +assertEq(asmLink(asmCompile('x', USE_ASM + 'function a(){} return a'))(), undefined); +assertEq(asmLink(asmCompile('x','y', USE_ASM + 'function a(){} return a'))(), undefined); +assertEq(asmLink(asmCompile('x','y','z', USE_ASM + 'function a(){} return a'))(), undefined); +assertAsmTypeFail('x','y', USE_ASM + 'function y(){} return f'); +assertEq(asmLink(asmCompile('x', USE_ASM + 'function f(){} return f'), 1, 2, 3)(), undefined); +assertEq(asmLink(asmCompile('x', USE_ASM + 'function f(){} return f'), 1)(), undefined); +assertEq(asmLink(asmCompile('x','y', USE_ASM + 'function f(){} return f'), 1, 2)(), undefined); + +assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) {i=i|0} return f'))(42), undefined); +assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0;var i} return f'); +assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0;var i=0} return f'); + +assertAsmTypeFail('glob', USE_ASM + 'var im=glob.imul; function f() {} return f'); +var code = asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'); +assertAsmLinkAlwaysFail(code, null); +assertAsmLinkAlwaysFail(code, {}); +assertAsmLinkAlwaysFail(code, {imul:Math.imul}); +assertEq(asmLink(code, {Math:{imul:Math.imul}})(2,3), 6); +var code = asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i,j) {i=i|0;j=j|0; return im(i,j)|0} return f'); +assertEq(asmLink(code, this)(8,4), 32); + +var code = asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'); +assertAsmLinkAlwaysFail(code, null, null); +assertAsmLinkAlwaysFail(code, this, null, null); +assertAsmLinkAlwaysFail(code, this, null, null); +assertAsmLinkAlwaysFail(code, this, null, new ArrayBuffer(1)); +assertAsmLinkFail(code, this, null, new ArrayBuffer(100)); +assertAsmLinkFail(code, this, null, new ArrayBuffer(4000)); +assertEq(asmLink(code, this, null, new ArrayBuffer(4096))(), undefined); +var code = asmCompile('glob','i','b', USE_ASM + 'var i32=new glob.Int32Array(b); function f(){} return f'); +assertEq(asmLink(code, this, null, new ArrayBuffer(2*4096))(), undefined); + +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i]|0; return i|0}; return f'); +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>1]|0; return i|0}; return f'); +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>1]|0; return i|0}; return f'); +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f'); +assertAsmLinkAlwaysFail(code, this, null, new ArrayBuffer(4095)); +assertEq(code(this, null, new ArrayBuffer(4096))(), 0); + +var exp = asmLink(asmCompile(USE_ASM + "return {}")); +assertEq(Object.keys(exp).length, 0); + +var exp = asmLink(asmCompile(USE_ASM + "function f() { return 3 } return {f:f,f:f}")); +assertEq(exp.f(), 3); +assertEq(Object.keys(exp).join(), 'f'); + +assertAsmTypeFail(USE_ASM + "function f() { return 3 } return {1:f}"); +assertAsmTypeFail(USE_ASM + "function f() { return 3 } return {get x() {} }"); + +var exp = asmLink(asmCompile(USE_ASM + 'function internal() { return ((g()|0)+2)|0 } function f() { return 1 } function g() { return 2 } function h() { return internal()|0 } return {f:f,g1:g,h1:h}')); +assertEq(exp.f(), 1); +assertEq(exp.g1(), 2); +assertEq(exp.h1(), 4); +assertEq(Object.keys(exp).join(), 'f,g1,h1'); + +// can't test destructuring args with Function constructor +function assertTypeFailInEval(str) +{ + var caught = false; + var oldOpts = options("werror"); + assertEq(oldOpts.indexOf("werror"), -1); + try { + eval(str); + } catch (e) { + assertEq((''+e).indexOf(ASM_TYPE_FAIL_STRING) == -1, false); + caught = true; + } + assertEq(caught, true); + options("werror"); +} +assertTypeFailInEval('function f({}) { "use asm"; function g() {} return g }'); +assertTypeFailInEval('function f({global}) { "use asm"; function g() {} return g }'); +assertTypeFailInEval('function f(global, {imports}) { "use asm"; function g() {} return g }'); diff --git a/js/src/jit-test/tests/asm.js/testCall.js b/js/src/jit-test/tests/asm.js/testCall.js new file mode 100644 index 00000000000..a8b7524e183 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testCall.js @@ -0,0 +1,49 @@ +load(libdir + "asm.js"); +load(libdir + "asserts.js"); + +assertAsmTypeFail(USE_ASM+"function f(){i=i|0} function g() { f(0) } return g"); +assertAsmTypeFail(USE_ASM+"function f(i){i=i|0} function g() { f() } return g"); +assertAsmTypeFail(USE_ASM+"function f(i,j,k,l){i=i|0;j=j|0;k=k|0;l=l|0} function g() { f(0,1,2) } return g"); +assertAsmTypeFail(USE_ASM+"function f(i,j,k,l){i=i|0;j=j|0;k=k|0;l=l|0} function g() { f(0,1,2,3,4) } return g"); +assertAsmTypeFail(USE_ASM+"function f(i){i=i|0} function g() { f(.1) } return g"); +assertAsmTypeFail(USE_ASM+"function f(i){i=+i} function g() { f(1) } return g"); +assertAsmTypeFail(USE_ASM+"function f(){return 0} function g() { var i=0.1; i=f() } return g"); +assertAsmTypeFail(USE_ASM+"function f(){return 0.1} function g() { var i=0; i=f() } return g"); + +assertEq(asmLink(asmCompile(USE_ASM+"function f() {return 42} function g() { return f()|0 } return g"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM+"function g(i) { i=i|0; return (i+2)|0 } function h(i) { i=i|0; return (g(i)+8)|0 } return h"))(50), 60); +assertEq(asmLink(asmCompile(USE_ASM+"function g(i) { i=i|0; return (i+2)|0 } function h(i) { i=i|0; return (g(i)+i)|0 } return h"))(50), 102); +assertEq(asmLink(asmCompile(USE_ASM+"function g(i) { i=+i; return +(i+.1) } function h(i) { i=+i; return +(g(i)+.2) } return h"))(20), 20+.1+.2); +assertEq(asmLink(asmCompile(USE_ASM+"function g(i,j) { i=i|0;j=j|0; return (i-j)|0 } function h(j,i) { j=j|0;i=i|0; return (g(i,j)+8)|0 } return h"))(10,20), 18); +assertEq(asmLink(asmCompile(USE_ASM+"function g(i,j) { i=i|0;j=+j; return +(+~~i+j) } function h(i,j) { i=i|0;j=+j; return +(g(i,j)+8.6) } return h"))(10, 1.5), 10+1.5+8.6); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0; return (n-o)|0 } return f"))(1,2,3,4,5,6,100), -94); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0; return (o-p)|0 } return f"))(1,2,3,4,5,6,100,20), 80); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0; return (((o+p)|0) + ((o+p)|0))|0 } return f"))(1,2,3,4,5,6,30,20), 100); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p,q,r) { i=+i;j=+j;k=+k;l=+l;m=+m;n=+n;o=+o;p=+p;q=+q;r=+r; return +(q-r) } return f"))(1,2,3,4,5,6,7,8,40.2,1.4), 40.2-1.4); + +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0; return (n-o)|0 } function g(i,j) { i=i|0;j=j|0; return f(0,0,0,0,0,i,j)|0 } return g"))(20,5), 15); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0; return (o-p)|0 } function g(i,j) { i=i|0;j=j|0; return f(0,0,0,0,0,0,i,j)|0 } return g"))(20,5), 15); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=+i;j=+j;k=+k;l=+l;m=+m;n=+n;o=+o;p=+p; return +(o-p) } function g(i,j) { i=+i;j=+j; return +f(0.0,0.0,0.0,0.0,0.0,0.0,i,j) } return g"))(.5, .1), .5-.1); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0; return (o-p)|0 } function g(i,j) { i=i|0;j=j|0; var k=0; k=(i+j)|0; return (f(0,0,0,0,0,0,i,j)+k)|0 } return g"))(20,10), 40); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=+i;j=+j;k=+k;l=+l;m=+m;n=+n;o=+o;p=+p; return +(o-p) } function g(i,j) { i=+i;j=+j; var k=0.1; k=i+j; return +(f(0.0,0.0,0.0,0.0,0.0,0.0,i,j)+k) } return g"))(.5, .1), (.5+.1)+(.5-.1)); + +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p,q) { i=i|0;j=j|0;k=k|0;l=l|0;m=+m;n=n|0;o=o|0;p=+p;q=q|0; return +((m-p) + +~~q) } function g(i,j,k) { i=+i;j=+j;k=k|0; return +f(0,0,0,0,j,0,0,i,k) } return g"))(.5, 20.1, 4), (20.1-.5)+4); + +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0; return (o-p)|0 } function g(i,j) { i=i|0;j=j|0; return f(0,0,0,0,0,0,f(0,0,0,0,0,0,i,j),j)|0 } return g"))(20,5), 10); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0; return (o-p)|0 } function g(i,j) { i=i|0;j=j|0; return f(0,0,0,0,0,0,f(0,0,0,0,0,0,i,j),f(0,0,0,0,0,0,j,i))|0 } return g"))(20,5), 30); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=+i;j=+j;k=+k;l=+l;m=+m;n=+n;o=+o;p=+p; return +(o-p) } function g(i,j) { i=+i;j=+j; return +f(0.0,0.0,0.0,0.0,0.0,0.0,f(0.0,0.0,0.0,0.0,0.0,0.0,i,j),j) } return g"))(10.3, .2), 10.3-.2-.2); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p) { i=+i;j=+j;k=+k;l=+l;m=+m;n=+n;o=+o;p=+p; return +(o-p) } function g(i,j) { i=+i;j=+j; return +f(0.0,0.0,0.0,0.0,0.0,0.0,f(0.0,0.0,0.0,0.0,0.0,0.0,i,j),f(0.0,0.0,0.0,0.0,0.0,0.0,j,i)) } return g"))(10.3, .2), (10.3-.2)-(.2-10.3)); +assertEq(asmLink(asmCompile(USE_ASM+"function f(i,j,k,l,m,n,o,p,q) { i=i|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0; return (o-p)|0 } function g(i,j) { i=i|0;j=j|0; return f(0,0,0,0,0,0,i,f(0,0,0,0,0,0,i,j,0),0)|0 } return g"))(20,5), 5); + +assertEq(asmLink(asmCompile(USE_ASM+"function f(i) {i=i|0; return i|0} function g() { return 42; return f(13)|0 } return g"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM+"function e() { return 42 } function f(i) { i=i|0; switch(i|0) { case 0: return e()|0; default: return 13 } return 0 } function g() { return f(0)|0 } return g"))(), 42); + +var rec = asmLink(asmCompile(USE_ASM+"function rec() { rec() } return rec")); +assertThrowsInstanceOf(rec, InternalError); + +var rec = asmLink(asmCompile(USE_ASM+"function rec(i) { i=i|0; if (!i) return 0; return (rec((i-1)|0)+1)|0 } return rec")); +assertEq(rec(100), 100); +assertEq(rec(1000), 1000); +assertThrowsInstanceOf(function() rec(100000000000), InternalError); +assertEq(rec(10000), 10000); diff --git a/js/src/jit-test/tests/asm.js/testCompoundPlusMinus.js b/js/src/jit-test/tests/asm.js/testCompoundPlusMinus.js new file mode 100644 index 00000000000..9b070541e30 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testCompoundPlusMinus.js @@ -0,0 +1,14 @@ +load(libdir + "asm.js"); + +assertAsmTypeFail(USE_ASM + "function f(i,j,k) { i=i|0;j=+j;k=+k; return (i+(j+k))|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f(i,j,k) { i=i|0;j=j|0;k=+k; return +((i+j)+k) } return f"); +assertAsmTypeFail('imp', USE_ASM + "var ffi=imp.ffi; function f(i) { i=i|0; return (i+ffi())|0 } return f"); + +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j,k) { i=i|0;j=j|0;k=k|0; return (i+j+k)|0 } return f"))(1,2,3), 6); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j,k) { i=i|0;j=j|0;k=k|0; return (i+j-k)|0 } return f"))(1,2,3), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j,k) { i=i|0;j=j|0;k=k|0; return (i-j+k)|0 } return f"))(1,2,3), 2); + +const INT32_MAX = Math.pow(2,31)-1; +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; return (i+i+i+i+i+i+i+i+i+i)|0 } return f"))(INT32_MAX), (10*INT32_MAX)|0); +const INT32_MIN = -Math.pow(2,31); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; return (i+i+i+i+i+i+i+i+i+i)|0 } return f"))(INT32_MIN), (10*INT32_MIN)|0); diff --git a/js/src/jit-test/tests/asm.js/testControlFlow.js b/js/src/jit-test/tests/asm.js/testControlFlow.js new file mode 100644 index 00000000000..aaad146e0e3 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testControlFlow.js @@ -0,0 +1,165 @@ +load(libdir + "asm.js"); + +assertAsmTypeFail(USE_ASM + "function f(i,j) { i=i|0;j=+j; if (i) return j; return j } return f"); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=+j; if (i) return j; return +~~i } return f"))(1,1.4), 1.4); +assertAsmTypeFail(USE_ASM + "function f(i,j) { i=i|0;j=j|0; if (i) return j^0; return i^0 } return f"); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; if (i) return j^0; return i|0 } return f"))(1,8), 8); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { while (0) {} return 0} return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { for (;0;) {} return 0} return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { do {} while(0); return 0} return f"))(), 0); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { while (0) ; return 0} return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { for (;0;) ; return 0} return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { do ; while(0); return 0} return f"))(), 0); + +assertAsmTypeFail(USE_ASM + "function f(d) {d=+d; while (d) {}; return 0} return f"); +assertAsmTypeFail(USE_ASM + "function f(d) {d=+d; for (;d;) {}; return 0} return f"); +assertAsmTypeFail(USE_ASM + "function f(d) {d=+d; do {} while (d); return 0} return f"); + +assertEq(asmLink(asmCompile(USE_ASM + "function f(j) {j=j|0; var i=0; while ((i|0) < (j|0)) i=(i+4)|0; return i|0} return f"))(6), 8); +assertEq(asmLink(asmCompile(USE_ASM + "function f(j) {j=j|0; var i=0; for (;(i|0) < (j|0);) i=(i+4)|0; return i|0} return f"))(6), 8); +assertEq(asmLink(asmCompile(USE_ASM + "function f(j) {j=j|0; var i=0; do { i=(i+4)|0; } while ((i|0) < (j|0)); return i|0} return f"))(6), 8); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { while(1) return 42; return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { for(;1;) return 42; return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { do return 42; while(1); return 0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while(1) { if (i) return 13; return 42 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(;1;) { if (i) return 13; return 42 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do { if (i) return 13; return 42 } while(1); return 0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while(1) { break; while(1) {} } return 42 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(;;) { break; for(;;) {} } return 42 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do { break; do {} while(1) {} } while(1); return 42 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=1; while(1) { if (i) return 42; return 13 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=1; for(;1;) { if (i) return 42; return 13 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=1; do { if (i) return 42; return 13 } while(1); return 0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while(1) { if (i) return 13; else return 42; return 13 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(;;) { if (i) return 13; else return 42; return 13 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do { if (i) return 13; else return 42; return 13 } while(1); return 0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while((i|0) < 3) { if (i) return 42; i=(i+1)|0 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(;(i|0) < 3;) { if (i) return 42; i=(i+1)|0 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do { if (i) return 42; i=(i+1)|0 } while((i|0) < 3); return 0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while((i|0) < 3) { if (!i) i=(i+1)|0; return 42 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(;(i|0) < 3;) { if (!i) i=(i+1)|0; return 42 } return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do { if (!i) i=(i+1)|0; return 42 } while((i|0) < 3); return 0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42; return i|0; while(1) {} return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42; return i|0; for(;1;) {} return 0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42; return i|0; do {} while(1); return 0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while((i|0) < 10) if ((i|0) == 4) break; else i=(i+1)|0; return i|0 } return f"))(), 4); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(; (i|0) < 10;) if ((i|0) == 4) break; else i=(i+1)|0; return i|0 } return f"))(), 4); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do if ((i|0) == 4) break; else i=(i+1)|0; while((i|0) < 10); return i|0 } return f"))(), 4); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0,sum=0; while ((i=(i+1)|0)<2) { sum=(sum+1)|0; if ((i&1)==0) continue; sum=(sum+100)|0 } return sum|0 } return f"))(), 101); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0,sum=0; for (;(i=(i+1)|0)<2;) { sum=(sum+1)|0; if ((i&1)==0) continue; sum=(sum+100)|0 } return sum|0 } return f"))(), 101); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0,sum=0; do { sum=(sum+1)|0; if ((i&1)==0) continue; sum=(sum+100)|0 } while((i=(i+1)|0)<2); return sum|0 } return f"))(), 102); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; x:a:y:while(1) { i=1; while(1) { i=2; break a; } i=3; } return i|0 } return f"))(), 2); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; x:a:y:for(;;) { i=1; while(1) { i=2; break a; } i=3; } return i|0 } return f"))(), 2); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; x:a:y:do { i=1; while(1) { i=2; break a; } i=3; } while(1); return i|0 } return f"))(), 2); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; a:b:while((i|0) < 5) { i=(i+1)|0; while(1) continue b; } return i|0 } return f"))(), 5); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; a:b:for(;(i|0) < 5;) { i=(i+1)|0; while(1) continue b; } return i|0 } return f"))(), 5); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; a:b:do { i=(i+1)|0; while(1) continue b; } while((i|0) < 5); return i|0 } return f"))(), 5); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; return 0; a:b:while((i|0) < 5) { i=(i+1)|0; while(1) continue b; } return i|0 } return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; return 0; a:b:for(;(i|0) < 5;) { i=(i+1)|0; while(1) continue b; } return i|0 } return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; return 0; a:b:do { i=(i+1)|0; while(1) continue b; } while((i|0) < 5); return i|0 } return f"))(), 0); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42; a:{ break a; i=2; } b:{ c:{ break b; i=3 } i=4 } return i|0 } return f"))(), 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; a:b:for(;(i|0) < 5;i=(i+1)|0) { while(1) continue b; } return i|0 } return f"))(), 5); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42,sum=0; for(i=1;(i|0)<4;i=(i+1)|0) sum=(sum+i)|0; return sum|0 } return f"))(), 6); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42,sum=0; for(i=1;(i|0)<8;i=(i+1)|0) { if ((i&1) == 0) continue; sum=(sum+i)|0; } return sum|0 } return f"))(), 16); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while(1) { i=(i+1)|0; if ((i|0) > 10) break; } return i|0 } return f"))(), 11); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(;1;i=(i+1)|0) { if ((i|0) > 10) break; } return i|0 } return f"))(), 11); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do { if ((i|0) > 10) break; i=(i+1)|0 } while(1); return i|0 } return f"))(), 11); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; while(1){ if ((i|0)>0) break; while (1) { i=i+1|0; if ((i|0)==1) break; } } return i|0; } return f"))(), 1); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; for(;;){ if ((i|0)>0) break; while (1) { i=i+1|0; if ((i|0)==1) break; } } return i|0; } return f"))(), 1); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; do{ if ((i|0)>0) break; while (1) { i=i+1|0; if ((i|0)==1) break; } }while(1); return i|0; } return f"))(), 1); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0,sum=0; while(1){ if ((i|0)>5) break; while (1) { i=i+1|0; sum=(sum+i)|0; if ((i|0)>3) break; } } return sum|0; } return f"))(), 21); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0,sum=0; for(;;){ if ((i|0)>5) break; while (1) { i=i+1|0; sum=(sum+i)|0; if ((i|0)>3) break; } } return sum|0; } return f"))(), 21); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0,sum=0; do{ if ((i|0)>5) break; while (1) { i=i+1|0; sum=(sum+i)|0; if ((i|0)>3) break; } }while(1); return sum|0; } return f"))(), 21); + +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; while(1) { if (i) { break; } else { return i|0 } i = 1 } return i|0 } return f"))(3), 3); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; for(;1;) { if (i) { break; } else { return i|0 } i = 1 } return i|0 } return f"))(3), 3); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; do { if (i) { break; } else { return i|0 } i = 1 } while (0); return i|0 } return f"))(3), 3); + +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; while(1) { if (i) { return i|0 } else { return i|0 } i = 1 } return i|0 } return f"))(3), 3); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; for(;;) { if (i) { return i|0 } else { return i|0 } i = 1 } return i|0 } return f"))(3), 3); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; do { if (i) { return i|0 } else { return i|0 } i = 1 } while (0); return i|0 } return f"))(3), 3); + +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; for(;;) { return i|0 } return 0 } return f"))(42), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f(n) { n=n|0; var i=0,s=0; for(;;i=(i+1)|0) { if (~~i==~~n) return s|0; s=(s+i)|0 } return 0 } return f"))(8), 28); + +var f = asmLink(asmCompile(USE_ASM + "function f(n,m) { n=n|0;m=m|0; var i=0,sum=0; while((n|0)>(m|0) ? ((i|0)<(n|0))|0 : ((i|0)<(m|0))|0) { sum = (sum+i)|0; i=(i+1)|0 } return sum|0 } return f")); +assertEq(f(1,5), 10); +assertEq(f(6,5), 15); + +var f = asmLink(asmCompile(USE_ASM + "function f(n,m) { n=n|0;m=m|0; var i=0,sum=0; for(; (n|0)>(m|0) ? ((i|0)<(n|0))|0 : ((i|0)<(m|0))|0; i=(i+1)|0) { sum = (sum+i)|0 } return sum|0 } return f")); +assertEq(f(1,5), 10); +assertEq(f(6,5), 15); + +var f = asmLink(asmCompile(USE_ASM + "function f(n,m) { n=n|0;m=m|0; var i=0,sum=0; do { sum = (sum+i)|0; i=(i+1)|0 } while((n|0)>(m|0) ? ((i|0)<(n|0))|0 : ((i|0)<(m|0))|0); return sum|0 } return f")); +assertEq(f(1,5), 10); +assertEq(f(6,5), 15); + +assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; switch(i|0) { case 1: return 0; case 1: return 0 } return 0} return f"); +assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; switch(i|0) { case 1: return 0; case 2: return 0; case 1: return 0 } return 0} return f"); +assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; switch(1) { case 1: return 0; case 1: return 0 } return 0} return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0; switch(i) {}; return i|0 } return f"); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; switch(i|0) {}; return i|0 } return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; switch(i|0) { default: i=42 } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; switch(i|0) { default: i=42; break } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; switch(i|0) { case 0: i=42 } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; switch(i|0) { case 0: i=42; break } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; switch(i|0) { case 0: default: i=42 } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=0; switch(i|0) { case 0: default: i=42; break } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=1; switch(i|0) { case 0: case 2: break; default: i=42 } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=1; switch(i|0) { case 0: case 2: break; default: i=42; break } return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return 42; switch(1) { case 1: return 13 } return 14 } return f"))(), 42); + +var exp = asmLink(asmCompile(USE_ASM + "var x=0; function a() { return x|0 } function b(i) { i=i|0; x=i } function c(i) { i=i|0; if (i) return b(i); } return {a:a,b:b,c:c}")); +assertEq(exp.c(10), undefined); +assertEq(exp.a(), 10); + +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; switch(i|0) { case 1: i=42; break; default: i=13 } return i|0 } return f")); +assertEq(f(-1), 13); +assertEq(f(0), 13); +assertEq(f(1), 42); + +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; switch(i|0) { case -1: i=42; break; default: i=13 } return i|0 } return f")); +assertEq(f(-1), 42); +assertEq(f(0), 13); +assertEq(f(1), 13); +assertEq(f(0xffffffff), 42); + +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; var sum=0; switch(i|0) { case -1: sum=(sum+1)|0; case 1: sum=(sum+1)|0; case 3: sum=(sum+1)|0; default: sum=(sum+100)|0; } return sum|0 } return f")); +assertEq(f(-1), 103); +assertEq(f(0), 100); +assertEq(f(1), 102); +assertEq(f(2), 100); +assertEq(f(3), 101); + +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; var sum=0; switch(i|0) { case -1: sum=10; break; case 1: sum=11; break; case 3: sum=12; break; default: sum=13; } return sum|0 } return f")); +assertEq(f(-1), 10); +assertEq(f(0), 13); +assertEq(f(1), 11); +assertEq(f(2), 13); +assertEq(f(3), 12); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=8,sum=0; a:for(; (i|0)<20; i=(i+1)|0) { switch(i&3) { case 0:case 1:sum=(sum+i)|0;break;case 2:sum=(sum+100)|0;continue;default:break a} sum=(sum+10)|0; } sum=(sum+1000)|0; return sum|0 } return f"))(), 1137); +assertEq(asmLink(asmCompile('g', USE_ASM + "function f() { g:{ return 42 } return 13 } return f"), null)(), 42); + +var imp = { ffi:function() { throw "Wrong" } }; +assertEq(asmLink(asmCompile('glob','imp', USE_ASM + "var ffi=imp.ffi; function f() { var i=0; return (i+1)|0; return ffi(i|0)|0 } return f"), null, imp)(), 1); diff --git a/js/src/jit-test/tests/asm.js/testDebugModeDisables.js b/js/src/jit-test/tests/asm.js/testDebugModeDisables.js new file mode 100644 index 00000000000..127decca4b3 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testDebugModeDisables.js @@ -0,0 +1,5 @@ +// |jit-test| debug + +load(libdir + "asm.js"); + +assertAsmTypeFail("'use asm'; function f() {} return f"); diff --git a/js/src/jit-test/tests/asm.js/testExpressions.js b/js/src/jit-test/tests/asm.js/testExpressions.js new file mode 100644 index 00000000000..d3d097f992e --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testExpressions.js @@ -0,0 +1,279 @@ +load(libdir + "asm.js"); + +assertAsmTypeFail(USE_ASM + "function f() { var i=0,j=0.0; return (i+j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0.0,j=0; return (i+j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0,j=0.0; return (i-j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0.0,j=0; return (i-j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0,j=0.0; return (i*j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0.0,j=0; return (i*j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0,j=0; return (i*j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0; return (i*1048576)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0; return (i*-1048576)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0,j=0.0; return (i/j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0.0,j=0; return (i/j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=1,j=1; return (i/j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0,j=0.0; return (i%j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0.0,j=0; return (i%j)|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f() { var i=0.0,j=0; return (i>>0); return +j } return f")); +assertEq(f(0), 0); +assertEq(f(INT32_MAX), INT32_MAX); +assertEq(f(UINT32_MAX), UINT32_MAX); + +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; return (-i)|0 } return f")); +assertEq(f(0), 0); +assertEq(f(-0), 0); +assertEq(f(1), -1); +assertEq(f(INT32_MAX), INT32_MIN+1); +assertEq(f(INT32_MIN), INT32_MIN); + +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=+i; return +(-i) } return f")); +assertEq(f(0), -0); +assertEq(f(-0), 0); +assertEq(f(-1), 1); +assertEq(f(1), -1); +assertEq(f(Math.pow(2,50)), -Math.pow(2,50)); +assertEq(f(1.54e20), -1.54e20); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return ((i|0) < (j|0))|0 } return f")); +assertEq(f(0, 1), 1); +assertEq(f(1, 0), 0); +assertEq(f(1, 1), 0); +assertEq(f(INT32_MIN, INT32_MAX), 1); +assertEq(f(INT32_MAX, INT32_MIN), 0); +assertEq(f(0, INT32_MAX), 1); +assertEq(f(INT32_MAX, 0), 0); +assertEq(f(INT32_MIN, 0), 1); +assertEq(f(0, INT32_MIN), 0); +assertEq(f(UINT32_MAX, 0), 1); +assertEq(f(0, UINT32_MAX), 0); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return ((i>>>0) < (j>>>0))|0 } return f")); +assertEq(f(0, 1), 1); +assertEq(f(1, 0), 0); +assertEq(f(1, 1), 0); +assertEq(f(INT32_MIN, INT32_MAX), 0); +assertEq(f(INT32_MAX, INT32_MIN), 1); +assertEq(f(0, INT32_MAX), 1); +assertEq(f(INT32_MAX, 0), 0); +assertEq(f(INT32_MIN, 0), 0); +assertEq(f(0, INT32_MIN), 1); +assertEq(f(UINT32_MAX, 0), 0); +assertEq(f(0, UINT32_MAX), 1); + +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k=0; k=(i|0)==(j|0); return k|0 } return f"))(1,2), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k=0; k=(i|0)!=(j|0); return k|0 } return f"))(1,2), 1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k=0; k=(i|0)<(j|0); return k|0 } return f"))(1,2), 1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k=0; k=(i|0)>(j|0); return k|0 } return f"))(1,2), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k=0; k=(i|0)<=(j|0); return k|0 } return f"))(1,2), 1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k=0; k=(i|0)>=(j|0); return k|0 } return f"))(1,2), 0); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return ((i|0)/(j|0))|0 } return f")); +assertEq(f(4,2), 2); +assertEq(f(3,2), 1); +assertEq(f(3,-2), -1); +assertEq(f(-3,-2), 1); +assertEq(f(0, -1), 0); +assertEq(f(0, INT32_MAX), 0); +assertEq(f(0, INT32_MIN), 0); +assertEq(f(INT32_MAX, 0), 0); +assertEq(f(INT32_MIN, 0), 0); +assertEq(f(-1, INT32_MAX), 0); +assertEq(f(-1, INT32_MIN), 0); +assertEq(f(INT32_MAX, -1), -INT32_MAX); +assertEq(f(INT32_MIN, -1), INT32_MIN); // !! +assertEq(f(INT32_MAX, INT32_MAX), 1); +assertEq(f(INT32_MAX, INT32_MIN), 0); +assertEq(f(INT32_MIN, INT32_MAX), -1); +assertEq(f(INT32_MIN, INT32_MIN), 1); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return ((i>>>0)/(j>>>0))|0 } return f")); +assertEq(f(4,2), 2); +assertEq(f(3,2), 1); +assertEq(f(3,-2), 0); +assertEq(f(-3,-2), 0); +assertEq(f(0, -1), 0); +assertEq(f(0, INT32_MAX), 0); +assertEq(f(0, INT32_MIN), 0); +assertEq(f(0, UINT32_MAX), 0); +assertEq(f(INT32_MAX, 0), 0); +assertEq(f(INT32_MIN, 0), 0); +assertEq(f(UINT32_MAX, 0), 0); +assertEq(f(-1, INT32_MAX), 2); +assertEq(f(-1, INT32_MIN), 1); +assertEq(f(-1, UINT32_MAX), 1); +assertEq(f(INT32_MAX, -1), 0); +assertEq(f(INT32_MIN, -1), 0); +assertEq(f(UINT32_MAX, -1), 1); +assertEq(f(INT32_MAX, INT32_MAX), 1); +assertEq(f(INT32_MAX, INT32_MIN), 0); +assertEq(f(UINT32_MAX, INT32_MAX), 2); +assertEq(f(INT32_MAX, UINT32_MAX), 0); +assertEq(f(UINT32_MAX, UINT32_MAX), 1); +assertEq(f(INT32_MIN, INT32_MAX), 1); +assertEq(f(INT32_MIN, UINT32_MAX), 0); +assertEq(f(INT32_MIN, INT32_MIN), 1); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k = 0; k = (i|0)%(j|0); return k|0 } return f")); +assertEq(f(4,2), 0); +assertEq(f(3,2), 1); +assertEq(f(3,-2), 1); +assertEq(f(-3,-2), -1); +assertEq(f(0, -1), 0); +assertEq(f(0, INT32_MAX), 0); +assertEq(f(0, INT32_MIN), 0); +assertEq(f(INT32_MAX, 0), 0); +assertEq(f(INT32_MIN, 0), 0); +assertEq(f(-1, INT32_MAX), -1); +assertEq(f(-1, INT32_MIN), -1); +assertEq(f(INT32_MAX, -1), 0); +assertEq(f(INT32_MIN, -1), 0); // !! +assertEq(f(INT32_MAX, INT32_MAX), 0); +assertEq(f(INT32_MAX, INT32_MIN), INT32_MAX); +assertEq(f(INT32_MIN, INT32_MAX), -1); +assertEq(f(INT32_MIN, INT32_MIN), 0); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k = 0; k = (i|0)%4; return k|0 } return f")); +assertEq(f(0), 0); +assertEq(f(-1), -1); +assertEq(f(-3), -3); +assertEq(f(-4), 0); +assertEq(f(INT32_MIN), 0); +assertEq(f(3), 3); +assertEq(f(4), 0); +assertEq(f(INT32_MAX), 3); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var k = 0; k = (i>>>0)%(j>>>0); return k|0 } return f")); +assertEq(f(4,2), 0); +assertEq(f(3,2), 1); +assertEq(f(3,-2), 3); +assertEq(f(-3,-2), -3); +assertEq(f(0, -1), 0); +assertEq(f(0, INT32_MAX), 0); +assertEq(f(0, INT32_MIN), 0); +assertEq(f(0, UINT32_MAX), 0); +assertEq(f(INT32_MAX, 0), 0); +assertEq(f(INT32_MIN, 0), 0); +assertEq(f(UINT32_MAX, 0), 0); +assertEq(f(-1, INT32_MAX), 1); +assertEq(f(-1, INT32_MIN), INT32_MAX); +assertEq(f(-1, UINT32_MAX), 0); +assertEq(f(INT32_MAX, -1), INT32_MAX); +assertEq(f(INT32_MIN, -1), INT32_MIN); +assertEq(f(UINT32_MAX, -1), 0); +assertEq(f(INT32_MAX, INT32_MAX), 0); +assertEq(f(INT32_MAX, INT32_MIN), INT32_MAX); +assertEq(f(UINT32_MAX, INT32_MAX), 1); +assertEq(f(INT32_MAX, UINT32_MAX), INT32_MAX); +assertEq(f(UINT32_MAX, UINT32_MAX), 0); +assertEq(f(INT32_MIN, INT32_MAX), 1); +assertEq(f(INT32_MIN, UINT32_MAX), INT32_MIN); +assertEq(f(INT32_MIN, INT32_MIN), 0); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (4 / 2)|0 } return f"))(), 2); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (3 / 2)|0 } return f"))(), 1); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (4 % 2)|0 } return f"))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (3 % 2)|0 } return f"))(), 1); + +assertAsmTypeFail(USE_ASM + "function f() { var i=42,j=1.1; return +(i?i:j) } return f"); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42,j=1.1; return +(i?+(i|0):j) } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42,j=1; return (i?i:j)|0 } return f"))(), 42); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return ((i|0)>(j|0)?(i+10)|0:(j+100)|0)|0 } return f")); +assertEq(f(2, 4), 104); +assertEq(f(-2, -4), 8); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j,k) { i=i|0;j=j|0;k=k|0; return ((i|0)>(j|0) ? (i|0)>(k|0) ? i : k : (j|0)>(k|0) ? j : k)|0 } return f")); +assertEq(f(1,2,3), 3); +assertEq(f(1,3,2), 3); +assertEq(f(2,1,3), 3); +assertEq(f(2,3,1), 3); +assertEq(f(3,1,2), 3); +assertEq(f(3,2,1), 3); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; var a=0,b=0; a=i>>>0 < 4294967292; b=(i|0) < -4; return (j ? a : b)|0 } return f")); +assertEq(f(1,true), 1); +assertEq(f(-1,true), 0); +assertEq(f(-5,true), 1); +assertEq(f(1,false), 0); +assertEq(f(-1,false), 0); +assertEq(f(-5,false), 1); + +assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return (i32[0]+1)|0 } return f"); +assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] + 1.0); } return f"); +new Float64Array(BUF_64KB)[0] = 2.3; +assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] - 1.0) } return f"), this, null, BUF_64KB)(), 2.3-1); +assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] * 2.0) } return f"), this, null, BUF_64KB)(), 2.3*2); +assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] / 2.0) } return f"), this, null, BUF_64KB)(), 2.3/2); +assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] % 2.0) } return f"), this, null, BUF_64KB)(), 2.3%2); +assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +-f64[0] } return f"), this, null, BUF_64KB)(), -2.3); +assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "var sqrt=glob.Math.sqrt; function f() { return +sqrt(f64[0]) } return f"), this, null, BUF_64KB)(), Math.sqrt(2.3)); +new Int32Array(BUF_64KB)[0] = 42; +assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "var imul=glob.Math.imul; function f() { return imul(i32[0], 2)|0 } return f"), this, null, BUF_64KB)(), 84); + +// beware ye phis of comparisons and integers +var f = asmLink(asmCompile(USE_ASM + "function g(i) { i=i|0; if (i) { i = ((i|0) == 2); } else { i=(i-1)|0 } return i|0; } return g ")); +assertEq(f(0), -1); +assertEq(f(1), 0); +assertEq(f(2), 1); +var f = asmLink(asmCompile(USE_ASM + "function g(i) { i=i|0; if (i) { i = !i } else { i=(i-1)|0 } return i|0; } return g ")); +assertEq(f(0), -1); +assertEq(f(1), 0); +assertEq(f(2), 0); + +// beware ye constant-evaluate of boolean-producing operators +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (4 | (2 == 2))|0 } return f"))(), 5); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (4 | (!2))|0 } return f"))(), 4); + +// get that order-of-operations right! +var buf = new ArrayBuffer(4096); +asmLink(asmCompile('glob','imp','buf', USE_ASM + "var i32=new glob.Int32Array(buf); var x=0; function a() { return x|0 } function b() { x=42; return 0 } function f() { i32[(b() & 0x3) >> 2] = a() } return f"), this, null, buf)(); +assertEq(new Int32Array(buf)[0], 42); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { var a=0,i=0; for (; ~~i!=4; i=(i+1)|0) { a = (a*5)|0; if (+(a>>>0) != 0.0) return 1; } return 0; } return f"))(), 0) diff --git a/js/src/jit-test/tests/asm.js/testFFI.js b/js/src/jit-test/tests/asm.js/testFFI.js new file mode 100644 index 00000000000..f3edbf5a08d --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testFFI.js @@ -0,0 +1,88 @@ +load(libdir + "asm.js"); +load(libdir + "asserts.js"); + +function ffi(a,b,c,d) { + return a+b+c+d; +} + +var f = asmLink(asmCompile('global','imp', USE_ASM + 'var ffi=imp.ffi; function g() { return 1 } function f() { var i=0; i=g(); return ((ffi(4,5,6,7)|0)+i)|0 } return f'), null, {ffi:ffi}); +assertEq(f(1), 23); + +var counter = 0; +function inc() { return counter++ } +function add1(x) { return x+1 } +function add2(x,y) { return x+y } +function add3(x,y,z) { return x+y+z } +function addN() { + var sum = 0; + for (var i = 0; i < arguments.length; i++) + sum += arguments[i]; + return sum; +} +var imp = { inc:inc, add1:add1, add2:add2, add3:add3, addN:addN }; + +assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { incc() } return f'); +assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { var i = 0; return (i + inc)|0 } return f'); +assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { inc = 0 } return f'); +assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return (inc() + 1)|0 } return f'); +assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return +((inc()|0) + 1.1) } return f'); +assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return +(inc() + 1.1) } return f'); +assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return (+inc() + 1)|0 } return f'); + +assertAsmLinkFail(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return inc()|0 } return f'), null, {}); +assertAsmLinkFail(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return inc()|0 } return f'), null, {inc:0}); +assertAsmLinkFail(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return inc()|0 } return f'), null, {inc:{}}); + +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function g() { inc() } return g'), null, imp)(), undefined); +assertEq(counter, 1); + +var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function g() { return inc()|0 } return g'), null, imp); +assertEq(f(), 1); +assertEq(counter, 2); +assertEq(f(), 2); +assertEq(counter, 3); + +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add1=imp.add1; function g(i) { i=i|0; return add1(i|0)|0 } return g'), null, imp)(9), 10); +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add3=imp.add3; function g() { var i=1,j=3,k=9; return add3(i|0,j|0,k|0)|0 } return g'), null, imp)(), 13); +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add3=imp.add3; function g() { var i=1.4,j=2.3,k=32.1; return +add3(i,j,k) } return g'), null, imp)(), 1.4+2.3+32.1); + +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add3=imp.add3; function f(i,j,k) { i=i|0;j=+j;k=k|0; return add3(i|0,j,k|0)|0 } return f'), null, imp)(1, 2.5, 3), 6); +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var addN=imp.addN; function f() { return +addN(1,2,3,4.1,5,6.1,7,8.1,9.1,10,11.1,12,13,14.1,15.1,16.1,17.1,18.1) } return f'), null, imp)(), 1+2+3+4.1+5+6.1+7+8.1+9.1+10+11.1+12+13+14.1+15.1+16.1+17.1+18.1); + +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add2=imp.add2; function f(i,j) { i=i|0;j=+j; return +(+(add2(i|0,1)|0) + +add2(j,1) + +add2(+~~i,j)) } return f'), null, imp)(2, 5.5), 3+(5.5+1)+(2+5.5)); +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var addN=imp.addN; function f(i,j) { i=i|0;j=+j; return +(+addN(i|0,j,3,j,i|0) + +addN() + +addN(j,j,j)) } return f'), null, imp)(1, 2.2), (1+2.2+3+2.2+1)+(2.2+2.2+2.2)); + +counter = 0; +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var addN=imp.addN,inc=imp.inc; function f() { return ((addN(inc()|0,inc(3.3)|0,inc()|0)|0) + (addN(inc(0)|0)|0))|0 } return f'), null, imp)(), 6); +assertEq(counter, 4); + +var recurse = function(i,j) { if (i == 0) return j; return f(i-1,j+1)+j } +imp.recurse = recurse; +var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var r=imp.recurse; function f(i,j) { i=i|0;j=+j; return +r(i|0,j) } return f'), null, imp); +assertEq(f(0,3.3), 3.3); +assertEq(f(1,3.3), 3.3+4.3); +assertEq(f(2,3.3), 3.3+4.3+5.3); + +function maybeThrow(i, j) { + if (i == 0) + throw j; + try { + return f(i-1, j); + } catch(e) { + assertEq(typeof e, "number"); + return e; + } +} +var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i, j) { i=i|0;j=j|0; return ffi(i|0, (j+1)|0)|0 } return f'), null, {ffi:maybeThrow}); +assertThrowsValue(function() { f(0,0) }, 1); +assertThrowsValue(function() { f(0,Math.pow(2,31)-1) }, -Math.pow(2,31)); +assertEq(f(1,0), 2); +assertEq(f(2,0), 3); +assertEq(f(3,0), 4); +assertEq(f(4,5), 10); + +var recurse = function(i,j) { if (i == 0) throw j; f(i-1,j) } +var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function g(i,j,k) { i=i|0;j=+j;k=k|0; if (!(k|0)) ffi(i|0,j)|0; else g(i, j+1.0, (k-1)|0) } function f(i,j) { i=i|0;j=+j; g(i,j,4) } return f'), null, {ffi:recurse}); +assertThrowsValue(function() { f(0,2.4) }, 2.4+4); +assertThrowsValue(function() { f(1,2.4) }, 2.4+8); +assertThrowsValue(function() { f(8,2.4) }, 2.4+36); diff --git a/js/src/jit-test/tests/asm.js/testFastHeapAccess.js b/js/src/jit-test/tests/asm.js/testFastHeapAccess.js new file mode 100644 index 00000000000..74bc49c1af4 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testFastHeapAccess.js @@ -0,0 +1,68 @@ +load(libdir + "asm.js"); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=j|0; u32[((i<<2)+32 & 0xffff)>>2] = j } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + f(i, i); +var u32 = new Uint32Array(BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(u32[8+i], i); +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return u32[((i<<2)+32 & 0xffff)>>2]|0 } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(f(i), i); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=j|0; u32[(i<<2 & 0xffff)>>2] = j } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + f(i, i); +var u32 = new Uint32Array(BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(u32[i], i); +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return u32[(i<<2 & 0xffff)>>2]|0 } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(f(i), i); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=j|0; u8[i+20 & 0xffff] = j } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + f(i, i); +var u8 = new Uint8Array(BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(u8[i+20], i); +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return u8[i+20 & 0xffff]|0 } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(f(i), i); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j,k) {i=i|0;j=j|0;k=k|0; i32[(i + (j<<2) & 0xffff) >> 2] = k } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + f(32, i, i); +var u32 = new Uint32Array(BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(u32[8+i], i); +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=j|0; return i32[(i + (j<<2) & 0xffff) >> 2]|0 } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(f(32, i), i); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j,k) {i=i|0;j=j|0;k=k|0; i32[(((i + (j<<2))|0) + 16 & 0xffff) >> 2] = k } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + f(32, i, i); +var u32 = new Uint32Array(BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(u32[8+i+4], i); +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=j|0; return i32[(((i + (j<<2))|0) + 16 & 0xffff) >> 2]|0 } return f'); +var f = asmLink(code, this, null, BUF_64KB); +for (var i = 0; i < 100; i++) + assertEq(f(32, i), i); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=j|0; return ((i32[(i+(j<<2)&0xffff)>>2]|0) + (i32[(((i+(j<<2))|0)+4&0xffff)>>2]|0))|0 } return f'); +var f = asmLink(code, this, null, BUF_64KB); +var i32 = new Uint32Array(BUF_64KB); +i32[11] = 3; +i32[12] = 97; +assertEq(f(12,8), 100); diff --git a/js/src/jit-test/tests/asm.js/testFloatingPoint.js b/js/src/jit-test/tests/asm.js/testFloatingPoint.js new file mode 100644 index 00000000000..56c5b83f832 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testFloatingPoint.js @@ -0,0 +1,126 @@ +load(libdir + "asm.js"); + +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return 1.1 } return f"))(), 1.1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; return +(+(i|0) + .1) } return f"))(1), 1.1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(d) { d=+d; return +d } return f"))(1.1), 1.1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return +(d+e) } return f"))(1.0, .1), 1.1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i,e) { i=i|0;e=+e; return +(+~~i+e) } return f"))(1, .1), 1.1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(d,i) { d=+d;i=i|0; return +(d + +(i|0)) } return f"))(.1, 1), 1.1); +assertEq(asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return +(d-e) } return f"))(1.1, .8), (1.1-.8)); +assertEq(asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return +(d*e) } return f"))(1.1, 2.2), (1.1*2.2)); + +var f = asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return (de)|0 } return f")); +assertEq(f(2.1, 1.1), 1); +assertEq(f(1.1, 1.1), 0); +assertEq(f(1.1, 2.1), 0); +assertEq(f(NaN, 1.1), 0); +assertEq(f(1.1, NaN), 0); +assertEq(f(NaN, NaN), 0); + +var f = asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return (d>=e)|0 } return f")); +assertEq(f(2.1, 1.1), 1); +assertEq(f(1.0, 1.1), 0); +assertEq(f(1.1, 2.1), 0); +assertEq(f(NaN, 1.1), 0); +assertEq(f(1.1, NaN), 0); +assertEq(f(NaN, NaN), 0); + +var f = asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return (d==e)|0 } return f")); +assertEq(f(2.1, 1.1), 0); +assertEq(f(1.1, 1.1), 1); +assertEq(f(1.1, 2.1), 0); +assertEq(f(NaN, 1.1), 0); +assertEq(f(1.1, NaN), 0); +assertEq(f(NaN, NaN), 0); + +var f = asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return (d!=e)|0 } return f")); +assertEq(f(2.1, 1.1), 1); +assertEq(f(1.1, 1.1), 0); +assertEq(f(1.1, 2.1), 1); +assertEq(f(NaN, 1.1), 1); +assertEq(f(1.1, NaN), 1); +assertEq(f(NaN, NaN), 1); + +var f = asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return +(d/e) } return f")); +assertEq(f(1.1, .1), (1.1/.1)); +assertEq(f(1.1, 0), (1.1/0)); +assertEq(f(1.1, -0), (1.1/-0)); + +var f = asmLink(asmCompile(USE_ASM + "function f(d,e) { d=+d;e=+e; return +(d%e) } return f")); +assertEq(f(1.1, .1), (1.1%.1)); +assertEq(f(1.1, 0), (1.1%0)); +assertEq(f(1.1, -0), (1.1%-0)); + +var f = asmLink(asmCompile(USE_ASM + "function f(d) { d=+d; var i = 0; i = ~~d; return i|0 } return f")); +assertEq(f(1.0), 1); +assertEq(f(1.9), 1); +assertEq(f(1.9999), 1); +assertEq(f(2.0), 2); +assertEq(f(Math.pow(2,40)), ~~Math.pow(2,40)); +assertEq(f(-Math.pow(2,40)), ~~-Math.pow(2,40)); +assertEq(f(4000000000), ~~4000000000); +assertEq(f(-4000000000), ~~-4000000000); +assertEq(f(NaN), 0); +assertEq(f(Infinity), 0); +assertEq(f(-Infinity), 0); + +assertAsmTypeFail(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return +((i|0)/(j|0)) } return f"); +assertAsmTypeFail(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return +(i+j) } return f"); +assertAsmTypeFail(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return +(i-j) } return f"); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return +(((i|0)/(j|0))|0) } return f")); +assertEq(f(1,0), 0); +assertEq(f(-Math.pow(2,31),-1), -Math.pow(2,31)); + +var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return +(((i|0)%(j|0))|0) } return f")); +assertEq(f(1,0), 0); +assertEq(f(-Math.pow(2,31),-1), 0); + +var buf = new ArrayBuffer(4096); +var f64 = new Float64Array(buf); +var i32 = new Int32Array(buf); +var u32 = new Uint32Array(buf); +var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[0] } return f'), this, null, buf); +f64[0] = 0; +assertEq(f(), 0); +f64[0] = -1; +assertEq(f(), -1); +f64[0] = 1; +assertEq(f(), 1); +f64[0] = Infinity; +assertEq(f(), Infinity); +f64[0] = -Infinity; +assertEq(f(), -Infinity); + +function ffi(d) { str = String(d) } +var g = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'var ffi=imp.ffi; function g() { ffi(+f64[0]) } return g'), this, {ffi:ffi}, buf); +var h = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function g() { return +(+f64[0] < 0.0 ? -+f64[0] : +f64[0]) } return g'), this, null, buf) + +// that sounds dangerous! +var a = [0,1,0xffff0000,0x7fff0000,0xfff80000,0x7ff80000,0xfffc0000,0x7ffc0000,0xffffffff,0x0000ffff,0x00008fff7]; +for (i of a) { + for (j of a) { + u32[0] = i; + u32[1] = j; + + assertEq(f(), f64[0]); + + g(); + assertEq(str, String(f64[0])); + + assertEq(h(), Math.abs(f64[0])); + } +} diff --git a/js/src/jit-test/tests/asm.js/testFunctionPtr.js b/js/src/jit-test/tests/asm.js/testFunctionPtr.js new file mode 100644 index 00000000000..2d455277704 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testFunctionPtr.js @@ -0,0 +1,49 @@ +load(libdir + "asm.js"); + +assertAsmTypeFail('imp', USE_ASM + "function f() {} var imp=[f]; return f"); +assertAsmTypeFail(USE_ASM + "function f() {} var eval=[f]; return f"); +assertAsmTypeFail(USE_ASM + "var tbl=0; function f() {} var tbl=[f]; return f"); +assertAsmTypeFail(USE_ASM + "function f() {} var tbl; return f"); +assertAsmTypeFail(USE_ASM + "function f() {} var tbl=[]; return f"); +assertAsmTypeFail(USE_ASM + "function f() {} var tbl=[f,f,f]; return f"); +assertAsmTypeFail(USE_ASM + "function f() {} var tbl=[1]; return f"); +assertAsmTypeFail(USE_ASM + "var g = 0; function f() {} var tbl=[g]; return f"); +assertAsmTypeFail(USE_ASM + "function f() {} function g(i) {i=i|0} var tbl=[f,g]; return f"); +assertAsmTypeFail(USE_ASM + "function f() {} function g() {return 0} var tbl=[f,g]; return f"); +assertAsmTypeFail(USE_ASM + "function f(i) {i=i|0} function g(i) {i=+i} var tbl=[f,g]; return f"); +assertAsmTypeFail(USE_ASM + "function f() {return 0} function g() {return 0.0} var tbl=[f,g]; return f"); +assertAsmTypeFail(USE_ASM + "var tbl=0; function g() {tbl[0&1]()} return g"); +assertEq(asmLink(asmCompile(USE_ASM + "function f() { return 42 } var tbl=[f]; return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 0} function g() {return 1} var tbl=[f,g]; return f"))(), 0); + +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return ([])[i&1]()|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return f[i&1]()|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return tbl[i]()|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return tbl[i&0]()|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return tbl[i&3]()|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g(i,j) { i=i|0;j=+j; return tbl[j&1]()|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return tbl[i&1](1)|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f(i) {i=i|0} function g(i) { i=i|0; return tbl[i&1]()|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f(i) {i=i|0} function g(i) { i=i|0; return tbl[i&1](3.0)|0 } var tbl=[f,f]; return g"); +assertAsmTypeFail(USE_ASM + "function f(d) {d=+d} function g(i) { i=i|0; return tbl[i&1](3)|0 } var tbl=[f,f]; return g"); +assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return tbl[i&1]()|0 } var tbl=[f,f]; return g"))(0), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl[i&1]()|0 } var tbl=[f,g]; return h"))(1), 13); +assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl2[i&1]()|0 } var tbl1=[f,g]; var tbl2=[g,f]; return h"))(1), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl2[i&3]()|0 } var tbl1=[f,g]; var tbl2=[g,g,g,f]; return h"))(3), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl1[i&1]()|0 } var tbl1=[f,g]; var tbl2=[g,g,g,f]; return h"))(1), 13); +assertEq(asmLink(asmCompile(USE_ASM + "var i=0,j=0; function f() {return i|0} function g() {return j|0} function h(x) { x=x|0; i=5;j=10; return tbl2[x&3]()|0 } var tbl1=[f,g]; var tbl2=[g,g,g,f]; return h"))(3), 5); +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + "var ffi=imp.ffi; function f() {return ffi()|0} function g() {return 13} function h(x) { x=x|0; return tbl2[x&3]()|0 } var tbl2=[g,g,g,f]; return h"), null, {ffi:function(){return 20}})(3), 20); +assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + "var ffi=imp.ffi; var i=0; function f() {return (~~ffi()+i)|0} function g() {return 13} function h(x) { x=x|0; i=2; return tbl2[x&3]()|0 } var tbl2=[g,g,g,f]; return h"), null, {ffi:function(){return 20}})(3), 22); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) {i=i|0; return +((i+1)|0)} function g(d) { d=+d; return +(d+2.5) } function h(i,j) { i=i|0;j=j|0; return +tbl2[i&1](tbl1[i&1](j)) } var tbl1=[f,f]; var tbl2=[g,g]; return h"))(0,10), 11+2.5); + +assertAsmTypeFail(USE_ASM + "function f() {return 42} function g() { return tbl[0]()|0 } var tbl=[f]; return g"); +assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() { return tbl[0&0]()|0 } var tbl=[f]; return g"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f1() {return 42} function f2() {return 13} function g() { return tbl[1&1]()|0 } var tbl=[f1,f2]; return g"))(), 13); + +var f = asmLink(asmCompile(USE_ASM + "function f1(d) {d=+d; return +(d/2.0)} function f2(d) {d=+d; return +(d+10.0)} function g(i,j) { i=i|0;j=+j; return +tbl[i&1](tbl[(i+1)&1](j)) } var tbl=[f1,f2]; return g")); +assertEq(f(0,10.2), (10.2+10)/2); +assertEq(f(1,10.2), (10.2/2)+10); + +var f = asmLink(asmCompile('glob','imp', USE_ASM + "var ffi=imp.ffi; function f(){return 13} function g(){return 42} function h(i) { i=i|0; var j=0; ffi(1); j=TBL[i&7](); ffi(1.5); return j|0 } var TBL=[f,g,f,f,f,f,f,f]; return h"), null, {ffi:function(){}}); +for (var i = 0; i < 100; i++) + assertEq(f(i), (i%8 == 1) ? 42 : 13); diff --git a/js/src/jit-test/tests/asm.js/testGlobals.js b/js/src/jit-test/tests/asm.js/testGlobals.js new file mode 100644 index 00000000000..b47731af0c6 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testGlobals.js @@ -0,0 +1,57 @@ +load(libdir + "asm.js"); + +assertAsmTypeFail(USE_ASM + "var i; function f(){} return f"); +assertEq(asmLink(asmCompile(USE_ASM + "var i=0; function f(){} return f"))(), undefined); +assertEq(asmLink(asmCompile(USE_ASM + "var i=42; function f(){ return i|0 } return f"))(), 42); +assertEq(asmLink(asmCompile(USE_ASM + "var i=4.2; function f(){ return +i } return f"))(), 4.2); +assertAsmTypeFail(USE_ASM + "var i=42; function f(){ return +(i+.1) } return f"); +assertAsmTypeFail(USE_ASM + "var i=1.2; function f(){ return i|0 } return f"); +assertAsmTypeFail(USE_ASM + "var i=0; function f(e){ e=+e; i=e } return f"); +assertAsmTypeFail(USE_ASM + "var d=0.1; function f(i){ i=i|0; d=i } return f"); +assertEq(asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; i=j; return i|0 } return f"))(42), 42); +assertEq(asmLink(asmCompile(USE_ASM + "var d=.1; function f(e) { e=+e; d=e; return +e } return f"))(42.1), 42.1); + +var f = asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; if ((j|0) != -1) { i=j } else { return i|0 } return 0 } return f")); +assertEq(f(-1), 13); +assertEq(f(42), 0); +assertEq(f(-1), 42); + +assertAsmTypeFail('global', USE_ASM + "var i=global; function f() { return i|0 } return f"); +assertAsmTypeFail('global', USE_ASM + "var i=global|0; function f() { return i|0 } return f"); +assertAsmTypeFail('global', USE_ASM + "var j=0;var i=j.i|0; function f() { return i|0 } return f"); +assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f"); +assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f"); +assertAsmTypeFail('global', USE_ASM + 'var i=global.Infinity; function f() { i = 0.0 } return f'); +var code = asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'); +assertAsmLinkAlwaysFail(code, undefined); +assertAsmLinkAlwaysFail(code, null); +assertAsmLinkFail(code, 3); +assertAsmLinkFail(code, {}); +assertAsmLinkFail(code, {Infinity:NaN}); +assertAsmLinkFail(code, {Infinity:-Infinity}); +assertEq(asmLink(code, {Infinity:Infinity})(), Infinity); +var code = asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'); +assertEq(asmLink(code, this)(), Infinity); +var code = asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'); +assertAsmLinkAlwaysFail(code, undefined); +assertAsmLinkAlwaysFail(code, null); +assertAsmLinkFail(code, 3); +assertAsmLinkFail(code, {}); +assertAsmLinkFail(code, {Infinity:Infinity}); +assertAsmLinkFail(code, {Infinity:-Infinity}); +assertEq(asmLink(code, {NaN:NaN})(), NaN); +var code = asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'); +assertEq(asmLink(code, this)(), NaN); + +assertAsmTypeFail('global', 'imp', USE_ASM + "var i=imp; function f() { return i|0 } return f"); +assertAsmTypeFail('global', 'imp', USE_ASM + "var j=0;var i=j.i|0; function f() { return i|0 } return f"); +assertAsmLinkAlwaysFail(asmCompile('global','imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), null, undefined); +assertAsmLinkAlwaysFail(asmCompile('global','imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), this, undefined); +assertAsmLinkAlwaysFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), null, null); +assertAsmLinkAlwaysFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), this, null); +assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), this, 42)(), 0); +assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f")(null, {i:42})), 42); +assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f")(null, {i:1.4})), 1); +assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(null, {i:42})), 42); +assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(this, {i:1.4})), 1.4); +assertEq(asmLink(asmCompile(USE_ASM + "var g=0; function f() { var i=42; while (1) { break; } g = i; return g|0 } return f"))(), 42); diff --git a/js/src/jit-test/tests/asm.js/testHeapAccess.js b/js/src/jit-test/tests/asm.js/testHeapAccess.js new file mode 100644 index 00000000000..22a4de27151 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js @@ -0,0 +1,141 @@ +load(libdir + "asm.js"); + +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { var x=0,y=0; return i8[x+y]|0 } return f'); +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { var x=0,y=0; return u8[x+y]|0 } return f'); + +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0>>0]|0 }; return f'); +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0>>1]|0 }; return f'); +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0>>4]|0 }; return f'); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0]|0 }; return f'), this, null, new ArrayBuffer(4096))(), 0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i = i32[i>>2]|0; return i|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0), 0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return i8[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7f),0x7f); +assertEq(f(0xff),-1); +assertEq(f(0x100),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return u8[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7f),0x7f); +assertEq(f(0xff),0xff); +assertEq(f(0x100),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return i16[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7fff),0x7fff); +assertEq(f(0xffff),-1); +assertEq(f(0x10000),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return u16[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7fff),0x7fff); +assertEq(f(0xffff),0xffff); +assertEq(f(0x10000),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return i32[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7fffffff),0x7fffffff); +assertEq(f(0xffffffff),-1); +assertEq(f(0x100000000),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return u32[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7fffffff),0x7fffffff); +assertEq(f(0xffffffff),-1); +assertEq(f(0x100000000),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return i8[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7f),0x7f); +assertEq(f(0xff),-1); +assertEq(f(0x100),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return u8[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7f),0x7f); +assertEq(f(0xff),0xff); +assertEq(f(0x100),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i8[0] = i; return i8[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7f),0x7f); +assertEq(f(0xff),-1); +assertEq(f(0x100),0); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i8[0] = i; return u8[0]|0}; return f'); +var f = asmLink(code, this, null, new ArrayBuffer(4096)); +assertEq(f(0),0); +assertEq(f(0x7f),0x7f); +assertEq(f(0xff),0xff); +assertEq(f(0x100),0); + +var i32 = new Int32Array(4096/4); +i32[0] = 13; +i32[1] = 0xfffeeee; +var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return i32[((i<<2)+1)>>2]|0 }; return f'), this, null, i32.buffer); +assertEq(f(0), 13); +assertEq(f(1), 0xfffeeee); +var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return i32[((i<<2)+2)>>2]|0 }; return f'), this, null, i32.buffer); +assertEq(f(0), 13); +assertEq(f(1), 0xfffeeee); +var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return i32[(i<<1)>>2]|0 }; return f'), this, null, i32.buffer); +assertEq(f(0), 13); +assertEq(f(1), 13); +assertEq(f(2), 0xfffeeee); +assertEq(f(3), 0xfffeeee); + +var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return i32[(i<<2)>>2]|0 }; return f'), this, null, i32.buffer); +assertEq(f(0), 13); +assertEq(f(1), 0xfffeeee); + +var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; return i32[((i<<2)+4)>>2]|0 }; return f'), this, null, i32.buffer); +assertEq(f(0), 0xfffeeee); + +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[7&0xffff] = 41 } return f'), this, null, BUF_64KB)(); +assertEq(new Uint8Array(BUF_64KB)[7], 41); +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[7&0xffff] = -41 } return f'), this, null, BUF_64KB)(); +assertEq(new Int8Array(BUF_64KB)[7], -41); +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[(6&0xffff)>>1] = 0xabc } return f'), this, null, BUF_64KB)(); +assertEq(new Uint16Array(BUF_64KB)[3], 0xabc); +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[(6&0xffff)>>1] = -0xabc } return f'), this, null, BUF_64KB)(); +assertEq(new Int16Array(BUF_64KB)[3], -0xabc); +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[(4&0xffff)>>2] = 0xabcde } return f'), this, null, BUF_64KB)(); +assertEq(new Uint32Array(BUF_64KB)[1], 0xabcde); +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[(4&0xffff)>>2] = -0xabcde } return f'), this, null, BUF_64KB)(); +assertEq(new Int32Array(BUF_64KB)[1], -0xabcde); +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[(4&0xffff)>>2] = 1.0 } return f'), this, null, BUF_64KB)(); +assertEq(new Float32Array(BUF_64KB)[1], 1.0); +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[(8&0xffff)>>3] = 1.3 } return f'), this, null, BUF_64KB)(); +assertEq(new Float64Array(BUF_64KB)[1], 1.3); + +new Float32Array(BUF_64KB)[1] = 1.0; +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(4&0xffff)>>2] } return f'), this, null, BUF_64KB)(), 1.0); +new Float64Array(BUF_64KB)[1] = 1.3; +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8&0xffff)>>3] } return f'), this, null, BUF_64KB)(), 1.3); + +asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u8[255]; u8[i] } return f'); +asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u8[i&0xff]; u8[255] } return f'); +asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[63]; u32[i>>2] } return f'); +asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[i>>2]; u32[63] } return f'); + +var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[64] } return f'); +asmLink(code, this, null, new ArrayBuffer(4096)); + +asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[12] = i } return f'), this, null, BUF_64KB)(11); +assertEq(new Int32Array(BUF_64KB)[12], 11); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[12]|0 } return f'), this, null, BUF_64KB)(), 11); +new Float64Array(BUF_64KB)[0] = 3.5; +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +-f64[0] } return f'), this, null, BUF_64KB)(), -3.5); diff --git a/js/src/jit-test/tests/asm.js/testLiterals.js b/js/src/jit-test/tests/asm.js/testLiterals.js new file mode 100644 index 00000000000..7d51b2e798a --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testLiterals.js @@ -0,0 +1,38 @@ +load(libdir + 'asm.js'); + +assertAsmTypeFail(USE_ASM + 'function f(d) { d=+d; var e=0; e=d; return +e } return f'); +assertAsmTypeFail(USE_ASM + 'function f(d) { d=+d; var e=1e1; e=d; return +e } return f'); +assertAsmTypeFail(USE_ASM + 'function f(d) { d=+d; var e=+0; e=d; return +e } return f'); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=0.0; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=-0.0; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=10.0; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=-10.0; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=1.0e2; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=-1.0e2; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=1.0e0; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=-1.0e0; e=d; return +e } return f'))(0.1), 0.1); +assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return 0.0 } function g() { var d=0.1; d=f(); return +d } return g'))(), 0); +assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return -0.0 } function g() { var d=0.1; d=f(); return +d } return g'))(), -0); +assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return 10.0 } function g() { var d=0.1; d=f(); return +d } return g'))(), 10); +assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return -10.0 } function g() { var d=0.1; d=f(); return +d } return g'))(), -10.0); + +assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1e10; j=i; return j|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1e100; j=i; return j|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1e10000; j=i; return j|0 } return f"); +assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1000000000000000000; j=i; return j|0 } return f"); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; var j=1e0; j=i; return j|0 } return f"))(42), 42); +assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; var j=1e9; j=i; return j|0 } return f"))(42), 42); + +assertAsmTypeFail(USE_ASM + 'function f() { var i=-2147483649; return i|0 } return f'); +assertAsmTypeFail(USE_ASM + 'function f() { var i=4294967296; return i|0 } return f'); +assertEq(asmLink(asmCompile(USE_ASM + 'function f() { var i=-2147483648; return i|0 } return f'))(), -2147483648); +assertEq(asmLink(asmCompile(USE_ASM + 'function f() { var i=4294967295; return i|0 } return f'))(), 4294967295|0); +assertAsmTypeFail(USE_ASM + 'function f(i) { i=i|0; return (i+-2147483649)|0 } return f'); +assertAsmTypeFail(USE_ASM + 'function f(i) { i=i|0; return (i+4294967296)|0 } return f'); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) { i=i|0; return (i+-2147483648)|0 } return f'))(0), -2147483648); +assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) { i=i|0; return (i+4294967295)|0 } return f'))(0), 4294967295|0); + +assertAsmTypeFail(USE_ASM + 'var i=-2147483649; function f() { return i|0 } return f'); +assertAsmTypeFail(USE_ASM + 'var i=4294967296; function f() { return i|0 } return f'); +assertEq(asmLink(asmCompile(USE_ASM + 'var i=-2147483648; function f() { return i|0 } return f'))(), -2147483648); +assertEq(asmLink(asmCompile(USE_ASM + 'var i=4294967295; function f() { return i|0 } return f'))(), 4294967295|0); diff --git a/js/src/jit-test/tests/asm.js/testMathLib.js b/js/src/jit-test/tests/asm.js/testMathLib.js new file mode 100644 index 00000000000..614e08e9428 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testMathLib.js @@ -0,0 +1,88 @@ +load(libdir + "asm.js"); + +function testUnary(f, g) { + var numbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000]; + for (n of numbers) + assertEq(f(n), g(n)); +} + +var code = asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'); +assertAsmLinkFail(code, {Math:{sin:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{sin:null}}); +testUnary(asmLink(code, {Math:{sin:Math.sin}}), Math.sin); + +var code = asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'); +assertAsmLinkFail(code, {Math:{cos:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{cos:null}}); +testUnary(asmLink(code, {Math:{cos:Math.cos}}), Math.cos); + +var code = asmCompile('glob', USE_ASM + 'var ta=glob.Math.tan; function f(d) { d=+d; return +ta(d) } return f'); +assertAsmLinkFail(code, {Math:{tan:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{tan:null}}); +testUnary(asmLink(code, {Math:{tan:Math.tan}}), Math.tan); + +var code = asmCompile('glob', USE_ASM + 'var as=glob.Math.asin; function f(d) { d=+d; return +as(d) } return f'); +assertAsmLinkFail(code, {Math:{asin:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{asin:null}}); +testUnary(asmLink(code, {Math:{asin:Math.asin}}), Math.asin); + +var code = asmCompile('glob', USE_ASM + 'var ac=glob.Math.acos; function f(d) { d=+d; return +ac(d) } return f'); +assertAsmLinkFail(code, {Math:{acos:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{acos:null}}); +testUnary(asmLink(code, {Math:{acos:Math.acos}}), Math.acos); + +var code = asmCompile('glob', USE_ASM + 'var at=glob.Math.atan; function f(d) { d=+d; return +at(d) } return f'); +assertAsmLinkFail(code, {Math:{atan:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{atan:null}}); +testUnary(asmLink(code, {Math:{atan:Math.atan}}), Math.atan); + +var code = asmCompile('glob', USE_ASM + 'var ce=glob.Math.ceil; function f(d) { d=+d; return +ce(d) } return f'); +assertAsmLinkFail(code, {Math:{ceil:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{ceil:null}}); +testUnary(asmLink(code, {Math:{ceil:Math.ceil}}), Math.ceil); + +var code = asmCompile('glob', USE_ASM + 'var fl=glob.Math.floor; function f(d) { d=+d; return +fl(d) } return f'); +assertAsmLinkFail(code, {Math:{floor:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{floor:null}}); +testUnary(asmLink(code, {Math:{floor:Math.floor}}), Math.floor); + +var code = asmCompile('glob', USE_ASM + 'var exq=glob.Math.exp; function f(d) { d=+d; return +exq(d) } return f'); +assertAsmLinkFail(code, {Math:{exp:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{exp:null}}); +testUnary(asmLink(code, {Math:{exp:Math.exp}}), Math.exp); + +var code = asmCompile('glob', USE_ASM + 'var lo=glob.Math.log; function f(d) { d=+d; return +lo(d) } return f'); +assertAsmLinkFail(code, {Math:{log:Math.sqrt}}); +assertAsmLinkFail(code, {Math:{log:null}}); +testUnary(asmLink(code, {Math:{log:Math.log}}), Math.log); + +var code = asmCompile('glob', USE_ASM + 'var sq=glob.Math.sqrt; function f(d) { d=+d; return +sq(d) } return f'); +assertAsmLinkFail(code, {Math:{sqrt:Math.sin}}); +assertAsmLinkFail(code, {Math:{sqrt:null}}); +testUnary(asmLink(code, {Math:{sqrt:Math.sqrt}}), Math.sqrt); + +var code = asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; return +abs(d) } return f'); +assertAsmLinkFail(code, {Math:{abs:Math.sin}}); +assertAsmLinkFail(code, {Math:{abs:null}}); +testUnary(asmLink(code, {Math:{abs:Math.abs}}), Math.abs); + +var f = asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; return abs(i|0)|0 } return f'), this); +for (n of [-Math.pow(2,31)-1, -Math.pow(2,31), -Math.pow(2,31)+1, -1, 0, 1, Math.pow(2,31)-2, Math.pow(2,31)-1, Math.pow(2,31)]) + assertEq(f(n), Math.abs(n|0)|0); + +function testBinary(f, g) { + var numbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000]; + for (n of numbers) + for (o of numbers) + assertEq(f(n,o), g(n,o)); +} + +var code = asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'); +assertAsmLinkFail(code, {Math:{pow:Math.sin}}); +assertAsmLinkFail(code, {Math:{pow:null}}); +testBinary(asmLink(code, {Math:{pow:Math.pow}}), Math.pow); + +var code = asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'); +assertAsmLinkFail(code, {Math:{atan2:Math.sin}}); +assertAsmLinkFail(code, {Math:{atan2:null}}); +testBinary(asmLink(code, {Math:{atan2:Math.atan2}}), Math.atan2); diff --git a/js/src/jit-test/tests/asm.js/testTimeout1.js b/js/src/jit-test/tests/asm.js/testTimeout1.js new file mode 100644 index 00000000000..9f3013c63eb --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testTimeout1.js @@ -0,0 +1,8 @@ +// |jit-test| exitstatus: 6; + +load(libdir + "asm.js"); + +var g = asmLink(asmCompile(USE_ASM + "function f() {} function g() { while(1) { f() } } return g")); +timeout(1); +g(); +assertEq(true, false); diff --git a/js/src/jit-test/tests/asm.js/testTimeout2.js b/js/src/jit-test/tests/asm.js/testTimeout2.js new file mode 100644 index 00000000000..4ba2d2624c8 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testTimeout2.js @@ -0,0 +1,8 @@ +// |jit-test| exitstatus: 6; + +load(libdir + "asm.js"); + +var g = asmLink(asmCompile(USE_ASM + "function g() { while(1) {} } return g")); +timeout(1); +g(); +assertEq(true, false); diff --git a/js/src/jit-test/tests/asm.js/testTimeout3.js b/js/src/jit-test/tests/asm.js/testTimeout3.js new file mode 100644 index 00000000000..4f7df0a5e82 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testTimeout3.js @@ -0,0 +1,8 @@ +// |jit-test| exitstatus: 6; + +load(libdir + "asm.js"); + +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; if (!i) return; f((i-1)|0); f((i-1)|0); f((i-1)|0); f((i-1)|0); f((i-1)|0); } return f")); +timeout(1); +f(100); +assertEq(true, false); diff --git a/js/src/jit-test/tests/asm.js/testTimeout4.js b/js/src/jit-test/tests/asm.js/testTimeout4.js new file mode 100644 index 00000000000..ebab85bc4b6 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testTimeout4.js @@ -0,0 +1,8 @@ +// |jit-test| exitstatus: 6; + +load(libdir + "asm.js"); + +var g = asmLink(asmCompile(USE_ASM + "function f(d) { d=+d; d=d*.1; d=d/.4; return +d } function g() { while(1) { f(1.1) } } return g")); +timeout(1); +g(); +assertEq(true, false); diff --git a/js/src/jit-test/tests/asm.js/testX86ByteStore.js b/js/src/jit-test/tests/asm.js/testX86ByteStore.js new file mode 100644 index 00000000000..b924e7d0d12 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testX86ByteStore.js @@ -0,0 +1,77 @@ +load(libdir + "asm.js"); + +var body = +" 'use asm';\ + var i8=new global.Int8Array(buffer);\ + function g(i,j,k) {\ + i=i|0;\ + j=j|0;\ + k=k|0;\ + var a=0,b=0,c=0,d=0,e=0,f=0;\ + a=(i+j)|0;\ + b=(k+j)|0;\ + c=(i+k)|0;\ + b=(a+b)|0;\ + d=(b+c+i+j)|0;\ + e=(a+j+c)|0;\ + f=(a+i+k)|0;\ + i8[i] = f;\ + return (a+b+c+d+e+f)|0;\ + }\ + return g;"; + +var buf=new ArrayBuffer(4096); +var g = asmLink(asmCompile('global','foreign','buffer',body), this, null, buf); +assertEq(g(1,2,3), 46); +assertEq(new Int8Array(buf)[1], 7); + +var body = +" 'use asm';\ + var i8=new global.Int8Array(buffer);\ + function g(i,j,k) {\ + i=i|0;\ + j=j|0;\ + k=k|0;\ + var a=0,b=0,c=0,d=0,e=0,f=0;\ + a=(i+j)|0;\ + b=(k+j)|0;\ + c=(i+k)|0;\ + b=(a+b)|0;\ + d=(b+c+i+j)|0;\ + e=(a+j+c)|0;\ + f=(a+i+k)|0;\ + i8[i] = e;\ + return (a+b+c+d+e+f)|0;\ + }\ + return g;"; + +var buf=new ArrayBuffer(4096); +var g = asmLink(asmCompile('global','foreign','buffer',body), this, null, buf); +assertEq(g(1,2,3), 46); +assertEq(new Int8Array(buf)[1], 9); + +var body = +" 'use asm';\ + var i8=new global.Int8Array(buffer);\ + function g(i,j,k) {\ + i=i|0;\ + j=j|0;\ + k=k|0;\ + var a=0,b=0,c=0,d=0,e=0,f=0,g=0;\ + a=(i+j)|0;\ + b=(k+j)|0;\ + c=(i+k)|0;\ + b=(a+b)|0;\ + d=(b+c+i+j)|0;\ + e=(a+j+c)|0;\ + f=(a+i+k)|0;\ + g=(f+j+b)|0;\ + i8[i] = g;\ + return (a+b+c+d+e+f+g)|0;\ + }\ + return g;"; + +var buf=new ArrayBuffer(4096); +var g = asmLink(asmCompile('global','foreign','buffer',body), this, null, buf); +assertEq(g(1,2,3), 63); +assertEq(new Int8Array(buf)[1], 17); diff --git a/js/src/jit-test/tests/asm.js/testZOOB.js b/js/src/jit-test/tests/asm.js/testZOOB.js new file mode 100644 index 00000000000..48979ca71ad --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testZOOB.js @@ -0,0 +1,96 @@ +load(libdir + "asm.js"); + +// constants +var buf = new ArrayBuffer(4096); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf)(), 0); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x80000000]|0 } return f'), this, null, buf)(), 0); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x8fffffff]|0 } return f'), this, null, buf)(), 0); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff]|0 } return f'), this, null, buf)(), 0); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-1]|0 } return f'), this, null, buf)(), 0); +assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-2]|0 } return f'), this, null, buf)(), 0); +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int16Array(b); function f() {return arr[-1]|0 } return f'); +assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[-2]|0 } return f'); + +function testInt(ctor, shift, scale, disp) { + var ab = new ArrayBuffer(4096); + var arr = new ctor(ab); + for (var i = 0; i < arr.length; i++) + arr[i] = i; + var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i) {i=i|0; return arr[((i<<' + scale + ')+' + disp + ')>>' + shift + ']|0 } return f'), this, null, ab); + for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097]) + assertEq(f(i), arr[((i<>shift]|0); + + for (var i of [-Math.pow(2,28),Math.pow(2,28),-Math.pow(2,29),Math.pow(2,29),-Math.pow(2,30),Math.pow(2,30),-Math.pow(2,31),Math.pow(2,31),-Math.pow(2,32),Math.pow(2,32)]) { + for (var j of [-8,-4,-1,0,1,4,8]) + assertEq(f(i+j), arr[(((i+j)<>shift]|0); + } + + var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i,j) {i=i|0;j=j|0; arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] = j } return f'), this, null, ab); + for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097]) { + var index = ((i<>shift; + var v = arr[index]|0; + arr[index] = 0; + f(i, v); + assertEq(arr[index]|0, v); + } + + for (var i of [-Math.pow(2,31), Math.pow(2,31)-1, Math.pow(2,32)]) { + for (var j of [-8,-4,-1,0,1,4,8]) { + var index = (((i+j)<>shift; + var v = arr[index]|0; + arr[index] = 0; + f(i+j, v); + assertEq(arr[index]|0, v); + } + } +} + +function testFloat(ctor, shift, scale, disp) { + var ab = new ArrayBuffer(4096); + var arr = new ctor(ab); + for (var i = 0; i < arr.length; i++) + arr[i] = i; + var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i) {i=i|0; return +arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] } return f'), this, null, ab); + for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097]) + assertEq(f(i), +arr[((i<>shift]); + + for (var i of [-Math.pow(2,31), Math.pow(2,31)-1, Math.pow(2,32)]) { + for (var j of [-8,-4,-1,0,1,4,8]) + assertEq(f(i+j), +arr[(((i+j)<>shift]); + } + + var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i,j) {i=i|0;j=+j; arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] = j } return f'), this, null, ab); + for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097]) { + var index = ((i<>shift; + var v = +arr[index]; + arr[index] = 0; + f(i, v); + assertEq(+arr[index], v); + } + + for (var i of [-Math.pow(2,31), Math.pow(2,31)-1, Math.pow(2,32)]) { + for (var j of [-8,-4,-1,0,1,4,8]) { + var index = (((i+j)<>shift; + var v = +arr[index]; + arr[index] = 0; + f(i+j, v); + assertEq(+arr[index], v); + } + } +} + +function test(tester, ctor, shift) { + for (scale of [0,1,2,3]) { + for (disp of [0,1,8,Math.pow(2,31)-1,Math.pow(2,31),Math.pow(2,32)-1]) + tester(ctor, shift, scale, disp); + } +} + +test(testInt, Int8Array, 0); +test(testInt, Uint8Array, 0); +test(testInt, Int16Array, 1); +test(testInt, Uint16Array, 1); +test(testInt, Int32Array, 2); +test(testInt, Uint32Array, 2); +test(testFloat, Float32Array, 2); +test(testFloat, Float64Array, 3); diff --git a/js/src/jit-test/tests/auto-regress/bug759312.js b/js/src/jit-test/tests/auto-regress/bug759312.js deleted file mode 100644 index e0647a9edce..00000000000 --- a/js/src/jit-test/tests/auto-regress/bug759312.js +++ /dev/null @@ -1,60 +0,0 @@ -// Binary: cache/js-dbg-32-4ce3983a43f4-linux -// Flags: --ion-eager -// -try { - gczeal(2); -var MyMath = { - random: function() { - this.seed = ((this.seed + 0x7ed55d16) + (this.seed << 12)) & 0xffffffff; - return (this.seed & 0xfffffff) / 0x10000000; - }, -}; -var kSplayTreeSize = 8000; -var kSplayTreePayloadDepth = 5; -function GeneratePayloadTree(depth, key) { - if (depth == 0) { - return { - array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], - string : 'String for key ' + key + ' in leaf node' - }; - } else { - return { - left: GeneratePayloadTree(depth - 1, key), - right: GeneratePayloadTree(depth - 1, key) - }; - } -} -function GenerateKey() { - return MyMath.random(); -} -function InsertNewNode() { - do { - key = GenerateKey(); - } while (splayTree.find(key) != null); - splayTree.insert(key, GeneratePayloadTree(kSplayTreePayloadDepth, key)); -} -function SplaySetup() { - splayTree = new SplayTree(); - for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode(); -} -function SplayTree() { -}; -SplayTree.prototype.isEmpty = function() { - return !this.root_; -}; -SplayTree.prototype.insert = function(key, value) { - if (this.isEmpty()) { - this.root_ = new SplayTree.Node(key, value); - } - var node = new SplayTree.Node(key, value); - if (key > this.root_.key) { - this.root_.left = null; - } - this.root_ = node; -}; -SplayTree.prototype.find = function(key) {}; -SplayTree.Node = function(key, value) { - this.key = key; -}; -SplaySetup(); -} catch(exc1) {} diff --git a/js/src/js.msg b/js/src/js.msg index c72d0952188..ddcae8145db 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -391,3 +391,7 @@ MSG_DEF(JSMSG_DATE_NOT_FINITE, 337, 0, JSEXN_RANGEERR, "date value is not MSG_DEF(JSMSG_MODULE_STATEMENT, 338, 0, JSEXN_SYNTAXERR, "module declarations may only appear at the top level of a program or module body") MSG_DEF(JSMSG_CURLY_BEFORE_MODULE, 339, 0, JSEXN_SYNTAXERR, "missing { before module body") MSG_DEF(JSMSG_CURLY_AFTER_MODULE, 340, 0, JSEXN_SYNTAXERR, "missing } after module body") +MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 341, 0, JSEXN_SYNTAXERR, "'use asm' directive only works on function code") +MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 342, 1, JSEXN_TYPEERR, "asm.js type error: {0}") +MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 343, 1, JSEXN_TYPEERR, "asm.js link error: {0}") +MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 344, 0, JSEXN_ERR, "Successfully compiled asm.js code") \ No newline at end of file diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 755c3603fb5..624a31035b0 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -341,6 +341,10 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) isJaegerInlineable = isIonInlineable = false; break; + case JSOP_LINKASMJS: + isJaegerCompileable = isIonInlineable = false; + break; + case JSOP_ENTERLET0: case JSOP_ENTERLET1: case JSOP_ENTERBLOCK: diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4e35a21ff6b..0a42178c639 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -684,7 +684,7 @@ JS_FRIEND_API(bool) JS::NeedRelaxedRootChecks() { return false; } static const JSSecurityCallbacks NullSecurityCallbacks = { }; -js::PerThreadData::PerThreadData(JSRuntime *runtime) +PerThreadData::PerThreadData(JSRuntime *runtime) : PerThreadDataFriendFields(), runtime_(runtime), #ifdef DEBUG @@ -694,9 +694,32 @@ js::PerThreadData::PerThreadData(JSRuntime *runtime) ionJSContext(NULL), ionStackLimit(0), ionActivation(NULL), + asmJSActivationStack_(NULL), +# ifdef JS_THREADSAFE + asmJSActivationStackLock_(NULL), +# endif suppressGC(0) {} +bool +PerThreadData::init() +{ +#ifdef JS_THREADSAFE + asmJSActivationStackLock_ = PR_NewLock(); + if (!asmJSActivationStackLock_) + return false; +#endif + return true; +} + +PerThreadData::~PerThreadData() +{ +#ifdef JS_THREADSAFE + if (asmJSActivationStackLock_) + PR_DestroyLock(asmJSActivationStackLock_); +#endif +} + JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) : mainThread(this), atomsCompartment(NULL), @@ -885,6 +908,9 @@ JSRuntime::init(uint32_t maxbytes) ownerThread_ = PR_GetCurrentThread(); #endif + if (!mainThread.init()) + return false; + js::TlsPerThreadData.set(&mainThread); #ifdef JS_METHODJIT_SPEW @@ -4801,6 +4827,11 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobjArg, JSRawObject parentArg return NULL; } + if (fun->hasScript() && fun->nonLazyScript()->asmJS) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CLONE_OBJECT); + return NULL; + } + return CloneFunctionObject(cx, fun, parent, fun->getAllocKind()); } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index e015a47555f..112372d927f 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2012,8 +2012,9 @@ JS_StringToVersion(const char *string); "use strict" annotations. */ #define JSOPTION_ION JS_BIT(20) /* IonMonkey */ +#define JSOPTION_ASMJS JS_BIT(21) /* optimizingasm.js compiler */ -#define JSOPTION_MASK JS_BITMASK(21) +#define JSOPTION_MASK JS_BITMASK(22) extern JS_PUBLIC_API(uint32_t) JS_GetOptions(JSContext *cx); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 646c57a1bab..cfdb26b6a1c 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -125,11 +125,12 @@ JSRuntime::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, RuntimeSizes *rtS rtSizes->temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); if (execAlloc_) { - execAlloc_->sizeOfCode(&rtSizes->jaegerCode, &rtSizes->ionCode, &rtSizes->regexpCode, - &rtSizes->unusedCode); + execAlloc_->sizeOfCode(&rtSizes->jaegerCode, &rtSizes->ionCode, &rtSizes->asmJSCode, + &rtSizes->regexpCode, &rtSizes->unusedCode); } else { rtSizes->jaegerCode = 0; rtSizes->ionCode = 0; + rtSizes->asmJSCode = 0; rtSizes->regexpCode = 0; rtSizes->unusedCode = 0; } @@ -153,9 +154,9 @@ JSRuntime::sizeOfExplicitNonHeap() size_t n = stackSpace.sizeOf(); if (execAlloc_) { - size_t jaegerCode, ionCode, regexpCode, unusedCode; - execAlloc_->sizeOfCode(&jaegerCode, &ionCode, ®expCode, &unusedCode); - n += jaegerCode + ionCode + regexpCode + unusedCode; + size_t jaegerCode, ionCode, asmJSCode, regexpCode, unusedCode; + execAlloc_->sizeOfCode(&jaegerCode, &ionCode, &asmJSCode, ®expCode, &unusedCode); + n += jaegerCode + ionCode + asmJSCode + regexpCode + unusedCode; } if (bumpAlloc_) @@ -180,6 +181,11 @@ JSRuntime::triggerOperationCallback() * immediately visible to other processors polling the flag. */ JS_ATOMIC_SET(&interrupt, 1); + +#ifdef JS_ION + /* asm.js code uses a separate mechanism to halt running code. */ + TriggerOperationCallbackForAsmJSCode(this); +#endif } void diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 10f8a8dcb79..326bd6c01a4 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -33,6 +33,7 @@ #include "gc/StoreBuffer.h" #include "js/HashTable.h" #include "js/Vector.h" +#include "ion/AsmJS.h" #include "vm/DateTime.h" #include "vm/SPSProfiler.h" #include "vm/Stack.h" @@ -480,6 +481,53 @@ class PerThreadData : public js::PerThreadDataFriendFields */ js::ion::IonActivation *ionActivation; + /* + * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This + * stack is used by JSRuntime::triggerOperationCallback to stop long- + * running asm.js without requiring dynamic polling operations in the + * generated code. Since triggerOperationCallback may run on a separate + * thread than the JSRuntime's owner thread all reads/writes must be + * synchronized (by asmJSActivationStackLock_). + */ + private: + friend class js::AsmJSActivation; + + /* See AsmJSActivation comment. */ + js::AsmJSActivation *asmJSActivationStack_; + +# ifdef JS_THREADSAFE + /* Synchronizes pushing/popping with triggerOperationCallback. */ + PRLock *asmJSActivationStackLock_; +# endif + + public: + static unsigned offsetOfAsmJSActivationStackReadOnly() { + return offsetof(PerThreadData, asmJSActivationStack_); + } + + class AsmJSActivationStackLock { + PerThreadData &data_; + public: +# ifdef JS_THREADSAFE + AsmJSActivationStackLock(PerThreadData &data) : data_(data) { + PR_Lock(data_.asmJSActivationStackLock_); + } + ~AsmJSActivationStackLock() { + PR_Unlock(data_.asmJSActivationStackLock_); + } +# else + AsmJSActivationStackLock(PerThreadData &data) : data_(data) {} + ~AsmJSActivationStackLock() {} +# endif + }; + + js::AsmJSActivation *asmJSActivationStackFromAnyThread() const { + return asmJSActivationStack_; + } + js::AsmJSActivation *asmJSActivationStackFromOwnerThread() const { + return asmJSActivationStack_; + } + /* * When this flag is non-zero, any attempt to GC will be skipped. It is used * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in @@ -491,6 +539,8 @@ class PerThreadData : public js::PerThreadDataFriendFields int32_t suppressGC; PerThreadData(JSRuntime *runtime); + ~PerThreadData(); + bool init(); bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; } }; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index fe73b0273ed..65c47771179 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -161,6 +161,7 @@ class JSFunction : public JSObject } JSAtom *atom() const { return hasGuessedAtom() ? NULL : atom_.get(); } + js::PropertyName *name() const { return hasGuessedAtom() ? NULL : atom_->asPropertyName(); } inline void initAtom(JSAtom *atom); JSAtom *displayAtom() const { return atom_; } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index adfe85de639..67e2d43ca99 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -4097,6 +4097,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferen case JSOP_TABLESWITCH: case JSOP_TRY: case JSOP_LABEL: + case JSOP_LINKASMJS: break; /* Bytecodes pushing values of known type. */ diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index c2942f9b158..cc13a374dca 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -41,6 +41,7 @@ #include "builtin/Eval.h" #include "gc/Marking.h" +#include "ion/AsmJS.h" #include "vm/Debugger.h" #include "vm/Shape.h" @@ -1311,9 +1312,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) /* No-ops for ease of decompilation. */ ADD_EMPTY_CASE(JSOP_NOP) ADD_EMPTY_CASE(JSOP_UNUSED71) -ADD_EMPTY_CASE(JSOP_UNUSED107) ADD_EMPTY_CASE(JSOP_UNUSED132) -ADD_EMPTY_CASE(JSOP_UNUSED147) ADD_EMPTY_CASE(JSOP_UNUSED148) ADD_EMPTY_CASE(JSOP_UNUSED161) ADD_EMPTY_CASE(JSOP_UNUSED162) @@ -1439,6 +1438,43 @@ BEGIN_CASE(JSOP_LOOPENTRY) END_CASE(JSOP_LOOPENTRY) +BEGIN_CASE(JSOP_LINKASMJS) +#ifdef JS_ASMJS +{ + RootedValue &rval = rootValue0; + + /* + * The callee specified "use asm" and the entire body type-checked + * according to the asm.js type system. However, there are still a set of + * dynamic checks required to validate the input parameters which are + * performed by LinkAsmJS. + */ + rval = NullValue(); + if (!LinkAsmJS(cx, regs.fp(), &rval)) + goto error; + + /* + * If the linking step succeeded, then 'rval' contains the result of + * executing the "use asm" function (i.e., the exported functions) so we + * can just return. + */ + if (rval.isObject()) { + regs.fp()->setReturnValue(rval); + regs.setToEndOfScript(); + interpReturnOK = true; + if (entryFrame != regs.fp()) + goto inline_return; + goto exit; + } + + /* + * Otherwise, linking failed. This will emit a warning, but is not + * otherwise a JS semantic error so we keep executing as normal. + */ +} +#endif +END_CASE(JSOP_LINKASMJS) + BEGIN_CASE(JSOP_NOTEARG) END_CASE(JSOP_NOTEARG) diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index f1b568c09f1..97f880fe98d 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -193,8 +193,8 @@ js::math_atan(JSContext *cx, unsigned argc, Value *vp) return JS_TRUE; } -static inline double JS_FASTCALL -math_atan2_kernel(double x, double y) +double +js::ecmaAtan2(double x, double y) { #if defined(_MSC_VER) /* @@ -223,8 +223,8 @@ math_atan2_kernel(double x, double y) return atan2(x, y); } -static JSBool -math_atan2(JSContext *cx, unsigned argc, Value *vp) +JSBool +js::math_atan2(JSContext *cx, unsigned argc, Value *vp) { double x, y, z; @@ -234,7 +234,7 @@ math_atan2(JSContext *cx, unsigned argc, Value *vp) } if (!ToNumber(cx, vp[2], &x) || !ToNumber(cx, vp[3], &y)) return JS_FALSE; - z = math_atan2_kernel(x, y); + z = ecmaAtan2(x, y); vp->setDouble(z); return JS_TRUE; } @@ -476,6 +476,9 @@ js::ecmaPow(double x, double y) */ if (!MOZ_DOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) return js_NaN; + /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */ + if (y == 0) + return 1; return pow(x, y); } #if defined(_MSC_VER) diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 4701ae07fe9..91150d809fc 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -7,6 +7,8 @@ #ifndef jsmath_h___ #define jsmath_h___ +#include "jsapi.h" + namespace js { typedef double (*UnaryFunType)(double); @@ -95,6 +97,9 @@ js_math_floor_impl(double x); namespace js { +extern JSBool +math_exp(JSContext *cx, unsigned argc, Value *vp); + extern JSBool math_imul(JSContext *cx, unsigned argc, js::Value *vp); @@ -128,6 +133,21 @@ math_tan(JSContext *cx, unsigned argc, js::Value *vp); extern double math_tan_impl(MathCache *cache, double x); +extern JSBool +math_asin(JSContext *cx, unsigned argc, Value *vp); + +extern JSBool +math_acos(JSContext *cx, unsigned argc, Value *vp); + +extern JSBool +math_atan(JSContext *cx, unsigned argc, Value *vp); + +extern JSBool +math_atan2(JSContext *cx, unsigned argc, Value *vp); + +extern double +ecmaAtan2(double x, double y); + extern double math_atan_impl(MathCache *cache, double x); @@ -152,6 +172,9 @@ powi(double x, int y); extern double ecmaPow(double x, double y); +extern JSBool +math_imul(JSContext *cx, unsigned argc, Value *vp); + } /* namespace js */ #endif /* jsmath_h___ */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index a986739f31d..85935411954 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -1019,8 +1019,13 @@ JSObject::finish(js::FreeOp *fop) { if (hasDynamicSlots()) fop->free_(slots); - if (hasDynamicElements()) - fop->free_(getElementsHeader()); + if (hasDynamicElements()) { + js::ObjectElements *elements = getElementsHeader(); + if (JS_UNLIKELY(elements->isAsmJSArrayBuffer())) + js::ArrayBufferObject::releaseAsmJSArrayBuffer(fop, this); + else + fop->free_(elements); + } } /* static */ inline bool @@ -1104,8 +1109,19 @@ JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, JS::ObjectsExtraSi if (hasDynamicSlots()) sizes->slots = mallocSizeOf(slots); - if (hasDynamicElements()) - sizes->elements = mallocSizeOf(getElementsHeader()); + if (hasDynamicElements()) { + js::ObjectElements *elements = getElementsHeader(); +#if defined (JS_CPU_X64) + // On x64, ArrayBufferObject::prepareForAsmJS switches the + // ArrayBufferObject to use mmap'd storage. This is not included in the + // total 'explicit' figure and thus we must not include it here. + // TODO: include it somewhere else. + if (JS_LIKELY(!elements->isAsmJSArrayBuffer())) + sizes->elements = mallocSizeOf(elements); +#else + sizes->elements = mallocSizeOf(elements); +#endif + } // Other things may be measured in the future if DMD indicates it is worthwhile. // Note that sizes->private_ is measured elsewhere. diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index af6b3790737..b9edb4a7303 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -251,7 +251,9 @@ OPDEF(JSOP_LEAVEFORLETIN, 105,"leaveforletin",NULL, 1, 0, 0, 0, JOF_BYTE) /* The argument is the offset to the next statement and is used by IonMonkey. */ OPDEF(JSOP_LABEL, 106,"label", NULL, 5, 0, 0, 0, JOF_JUMP) -OPDEF(JSOP_UNUSED107, 107,"unused107", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* Performs the dynamic validation step of type-checked asm.js. */ +OPDEF(JSOP_LINKASMJS, 107,"linkasmjs", NULL, 1, 0, 0, 0, JOF_BYTE) /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */ OPDEF(JSOP_FUNCALL, 108,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 501ab51db45..dbb0e42d691 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -2396,6 +2396,7 @@ js::CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction c RootedScript script(cx, clone->nonLazyScript()); JS_ASSERT(script); + JS_ASSERT(!script->asmJS); JS_ASSERT(script->compartment() == original->compartment()); JS_ASSERT_IF(script->compartment() != cx->compartment, !script->enclosingStaticScope()); @@ -2707,6 +2708,8 @@ JSScript::markChildren(JSTracer *trc) #ifdef JS_ION ion::TraceIonScripts(trc, this); + if (asmJS) + MarkObject(trc, &asmJS, "asmJS"); #endif } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index cce6292ea28..7c196ba7871 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -387,6 +387,14 @@ class JSScript : public js::gc::Cell // function; otherwise the enclosing scope js::HeapPtrObject enclosingScopeOrOriginalFunction_; + public: + // We should be able to remove this soon. + js::HeapPtr asmJS; + +#if JS_BYTES_PER_WORD == 4 + uint32_t PADDING32; +#endif + // 32-bit fields. public: diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 1360be0ad5a..fec8e205175 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -39,6 +39,12 @@ #include "vm/GlobalObject-inl.h" +# ifdef XP_WIN +# include "jswin.h" +# else +# include +# endif + using namespace js; using namespace js::gc; using namespace js::types; @@ -274,7 +280,7 @@ GetViewList(ArrayBufferObject *obj) } void -ArrayBufferObject::changeContents(ObjectElements *newHeader) +ArrayBufferObject::changeContents(JSContext *maybecx, ObjectElements *newHeader) { // Grab out data before invalidating it. uint32_t byteLengthCopy = byteLength(); @@ -286,6 +292,10 @@ ArrayBufferObject::changeContents(ObjectElements *newHeader) for (JSObject *view = viewListHead; view; view = NextView(view)) { uintptr_t newDataPtr = uintptr_t(view->getPrivate()) - oldDataPointer + newDataPointer; view->setPrivate(reinterpret_cast(newDataPtr)); + + // Notify compiled jit code that the base pointer has moved. + if (maybecx) + MarkObjectStateChange(maybecx, view); } // Change to the new header (now, so we can use GetViewList). @@ -306,10 +316,134 @@ ArrayBufferObject::uninlineData(JSContext *maybecx) if (!newHeader) return false; - changeContents(newHeader); + changeContents(maybecx, newHeader); return true; } +#if defined(JS_ION) && defined(JS_CPU_X64) +// To avoid dynamically checking bounds on each load/store, asm.js code relies +// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works +// if we can guarantee that *any* out-of-bounds access generates a fault. This +// isn't generally true since an out-of-bounds access could land on other +// Mozilla data. To overcome this on x64, we reserve an entire 4GB space, +// making only the range [0, byteLength) accessible, and use a 32-bit unsigned +// index into this space. (x86 and ARM require different tricks.) +// +// One complication is that we need to put an ObjectElements struct immediately +// before the data array (as required by the general JSObject data structure). +// Thus, we must stick a page before the elements to hold ObjectElements. +// +// |<------------------------------ 4GB + 1 pages --------------------->| +// |<--- sizeof --->|<------------------- 4GB ----------------->| +// +// | waste | ObjectElements | data array | inaccessible reserved memory | +// ^ ^ ^ +// | \ / +// obj->elements required to be page boundaries +// +JS_STATIC_ASSERT(sizeof(ObjectElements) < PageSize); +JS_STATIC_ASSERT(AsmJSAllocationGranularity == PageSize); +static const size_t AsmJSMappedSize = PageSize + AsmJSBufferProtectedSize; + +bool +ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle buffer) +{ + if (buffer->isAsmJSArrayBuffer()) + return true; + + // Get the entire reserved region (with all pages inaccessible). + void *p; +# ifdef XP_WIN + p = VirtualAlloc(NULL, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS); + if (!p) + return false; +# else + p = mmap(NULL, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (p == MAP_FAILED) + return false; +# endif + + // Enable access to the valid region. + JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0); +# ifdef XP_WIN + if (!VirtualAlloc(p, PageSize + buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) + return false; +# else + if (mprotect(p, PageSize + buffer->byteLength(), PROT_READ | PROT_WRITE)) + return false; +# endif + + // Copy over the current contents of the typed array. + uint8_t *data = reinterpret_cast(p) + PageSize; + memcpy(data, buffer->dataPointer(), buffer->byteLength()); + + // Swap the new elements into the ArrayBufferObject. + ObjectElements *newHeader = reinterpret_cast(data - sizeof(ObjectElements)); + ObjectElements *oldHeader = buffer->hasDynamicElements() ? buffer->getElementsHeader() : NULL; + buffer->changeContents(cx, newHeader); + js_free(oldHeader); + + // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not + // to js_free the header in the normal way. + newHeader->setIsAsmJSArrayBuffer(); + JS_ASSERT(data == buffer->dataPointer()); + return true; +} + +void +ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, RawObject obj) +{ + ArrayBufferObject &buffer = obj->asArrayBuffer(); + JS_ASSERT(buffer.isAsmJSArrayBuffer()); + + uint8_t *p = buffer.dataPointer() - PageSize ; + JS_ASSERT(uintptr_t(p) % PageSize == 0); +# ifdef XP_WIN + VirtualAlloc(p, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS); +# else + munmap(p, AsmJSMappedSize); +# endif +} + +void +ArrayBufferObject::neuterAsmJSArrayBuffer(ArrayBufferObject &buffer) +{ + // Protect all the pages so that any read/write will generate a fault which + // the AsmJSMemoryFaultHandler will turn into the expected result value. + JS_ASSERT(buffer.isAsmJSArrayBuffer()); + JS_ASSERT(buffer.byteLength() % AsmJSAllocationGranularity == 0); +#ifdef XP_WIN + if (!VirtualAlloc(buffer.dataPointer(), buffer.byteLength(), MEM_RESERVE, PAGE_NOACCESS)) + MOZ_CRASH(); +#else + if (mprotect(buffer.dataPointer(), buffer.byteLength(), PROT_NONE)) + MOZ_CRASH(); +#endif +} +#else /* defined(JS_ION) && defined(JS_CPU_X64) */ +bool +ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle buffer) +{ + if (!buffer->uninlineData(cx)) + return false; + + buffer->getElementsHeader()->setIsAsmJSArrayBuffer(); + return true; +} + +void +ArrayBufferObject::releaseAsmJSArrayBuffer(FreeOp *fop, RawObject obj) +{ + fop->free_(obj->asArrayBuffer().getElementsHeader()); +} + +void +ArrayBufferObject::neuterAsmJSArrayBuffer(ArrayBufferObject &buffer) +{ + // TODO: be ever-so-slightly unsound (but safe) for now. +} +#endif + #ifdef JSGC_GENERATIONAL class WeakObjectSlotRef : public js::gc::BufferableRef { @@ -472,7 +606,7 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents, ArrayBufferObject &buffer = obj->asArrayBuffer(); JSObject *views = *GetViewList(&buffer); js::ObjectElements *header = js::ObjectElements::fromElements((js::HeapSlot*)buffer.dataPointer()); - if (buffer.hasDynamicElements()) { + if (buffer.hasDynamicElements() && !buffer.isAsmJSArrayBuffer()) { *GetViewList(&buffer) = NULL; *contents = header; *data = buffer.dataPointer(); @@ -491,6 +625,9 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents, ArrayBufferObject::setElementsHeader(newheader, length); *contents = newheader; *data = reinterpret_cast(newheader + 1); + + if (buffer.isAsmJSArrayBuffer()) + ArrayBufferObject::neuterAsmJSArrayBuffer(buffer); } // Neuter the donor ArrayBuffer and all views of it @@ -3673,6 +3810,39 @@ js_InitTypedArrayClasses(JSContext *cx, HandleObject obj) return InitArrayBufferClass(cx); } +bool +js::IsTypedArrayConstructor(const Value &v, uint32_t type) +{ + switch (type) { + case TypedArray::TYPE_INT8: + return IsNativeFunction(v, Int8Array::class_constructor); + case TypedArray::TYPE_UINT8: + return IsNativeFunction(v, Uint8Array::class_constructor); + case TypedArray::TYPE_INT16: + return IsNativeFunction(v, Int16Array::class_constructor); + case TypedArray::TYPE_UINT16: + return IsNativeFunction(v, Uint16Array::class_constructor); + case TypedArray::TYPE_INT32: + return IsNativeFunction(v, Int32Array::class_constructor); + case TypedArray::TYPE_UINT32: + return IsNativeFunction(v, Uint32Array::class_constructor); + case TypedArray::TYPE_FLOAT32: + return IsNativeFunction(v, Float32Array::class_constructor); + case TypedArray::TYPE_FLOAT64: + return IsNativeFunction(v, Float64Array::class_constructor); + case TypedArray::TYPE_UINT8_CLAMPED: + return IsNativeFunction(v, Uint8ClampedArray::class_constructor); + } + JS_NOT_REACHED("unexpected typed array type"); + return false; +} + +bool +js::IsTypedArrayBuffer(const Value &v) +{ + return v.isObject() && v.toObject().isArrayBuffer(); +} + /* JS Friend API */ JS_FRIEND_API(JSBool) diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h index 140278956f4..e38313b1629 100644 --- a/js/src/jstypedarray.h +++ b/js/src/jstypedarray.h @@ -146,7 +146,7 @@ class ArrayBufferObject : public JSObject void addView(RawObject view); bool allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents = NULL); - void changeContents(ObjectElements *newHeader); + void changeContents(JSContext *cx, ObjectElements *newHeader); /* * Ensure that the data is not stored inline. Used when handing back a @@ -164,6 +164,10 @@ class ArrayBufferObject : public JSObject */ inline bool hasData() const; + inline bool isAsmJSArrayBuffer() const; + static bool prepareForAsmJS(JSContext *cx, Handle buffer); + static void neuterAsmJSArrayBuffer(ArrayBufferObject &buffer); + static void releaseAsmJSArrayBuffer(FreeOp *fop, RawObject obj); }; /* @@ -308,6 +312,34 @@ IsTypedArrayProtoClass(const Class *clasp) clasp < &TypedArray::protoClasses[TypedArray::TYPE_MAX]; } +bool +IsTypedArrayConstructor(const Value &v, uint32_t type); + +bool +IsTypedArrayBuffer(const Value &v); + +static inline unsigned +TypedArrayShift(ArrayBufferView::ViewType viewType) +{ + switch (viewType) { + case ArrayBufferView::TYPE_INT8: + case ArrayBufferView::TYPE_UINT8: + return 0; + case ArrayBufferView::TYPE_INT16: + case ArrayBufferView::TYPE_UINT16: + return 1; + case ArrayBufferView::TYPE_INT32: + case ArrayBufferView::TYPE_UINT32: + case ArrayBufferView::TYPE_FLOAT32: + return 2; + case ArrayBufferView::TYPE_FLOAT64: + return 3; + default:; + } + JS_NOT_REACHED("Unexpected array type"); + return 0; +} + class DataViewObject : public JSObject, public BufferView { public: diff --git a/js/src/jstypedarrayinlines.h b/js/src/jstypedarrayinlines.h index 2758e10bd21..22e30038b94 100644 --- a/js/src/jstypedarrayinlines.h +++ b/js/src/jstypedarrayinlines.h @@ -64,6 +64,12 @@ ArrayBufferObject::hasData() const return getClass() == &ArrayBufferClass; } +inline bool +ArrayBufferObject::isAsmJSArrayBuffer() const +{ + return getElementsHeader()->isAsmJSArrayBuffer(); +} + static inline int32_t ClampIntForUint8Array(int32_t x) { diff --git a/js/src/jswin.h b/js/src/jswin.h index bea1ebcd64e..50b548cb8fd 100644 --- a/js/src/jswin.h +++ b/js/src/jswin.h @@ -14,4 +14,5 @@ # undef STRICT # undef LEGACY # undef THIS +# undef PASSTHROUGH #endif diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index ee1be4bcaef..45d038a9047 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1249,6 +1249,10 @@ mjit::Compiler::jsop_setelem_typed(int atype) int32_t length = (int32_t) TypedArray::length(array); void *data = TypedArray::viewData(array); + // The 'data' pointer can change in rare circumstances + // (ArrayBufferObject::changeContents). + types::HeapTypeSet::WatchObjectStateChange(cx, array->getType(cx)); + objReg = frame.allocReg(); if (key.isConstant()) { @@ -1850,6 +1854,10 @@ mjit::Compiler::jsop_getelem_typed(int atype) int32_t length = (int32_t) TypedArray::length(array); void *data = TypedArray::viewData(array); + // The 'data' pointer can change in rare circumstances + // (ArrayBufferObject::changeContents). + types::HeapTypeSet::WatchObjectStateChange(cx, array->getType(cx)); + objReg = frame.allocReg(); if (key.isConstant()) { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index b823d51844a..1b1bdb34000 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -122,6 +122,7 @@ static bool enableMethodJit = true; static bool enableTypeInference = true; static bool enableDisassemblyDumps = false; static bool enableIon = true; +static bool enableAsmJS = true; static bool printTiming = false; @@ -4720,6 +4721,8 @@ NewContext(JSRuntime *rt) JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE); if (enableIon) JS_ToggleOptions(cx, JSOPTION_ION); + if (enableAsmJS) + JS_ToggleOptions(cx, JSOPTION_ASMJS); return cx; } @@ -4886,6 +4889,10 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op) enableIon = false; JS_ToggleOptions(cx, JSOPTION_ION); } + if (op->getBoolOption("no-asmjs")) { + enableAsmJS = false; + JS_ToggleOptions(cx, JSOPTION_ASMJS); + } if (const char *str = op->getStringOption("ion-gvn")) { if (strcmp(str, "off") == 0) @@ -5179,6 +5186,7 @@ main(int argc, char **argv, char **envp) #endif || !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)") || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") + || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") || !op.addStringOption('\0', "ion-gvn", "[mode]", "Specify Ion global value numbering:\n" " off: disable GVN\n" diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index ee417bca037..23b3b422159 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -124,6 +124,7 @@ macro(unwatch, unwatch, "unwatch") \ macro(url, url, "url") \ macro(useStrict, useStrict, "use strict") \ + macro(useAsm, useAsm, "use asm") \ macro(value, value, "value") \ macro(valueOf, valueOf, "valueOf") \ macro(var, var, "var") \ diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index 367628aa1a3..96654469a97 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -918,7 +918,8 @@ class ObjectElements { public: enum Flags { - CONVERT_DOUBLE_ELEMENTS = 0x1 + CONVERT_DOUBLE_ELEMENTS = 0x1, + ASMJS_ARRAY_BUFFER = 0x2 }; private: @@ -961,6 +962,12 @@ class ObjectElements void setShouldConvertDoubleElements() { flags |= CONVERT_DOUBLE_ELEMENTS; } + bool isAsmJSArrayBuffer() const { + return flags & ASMJS_ARRAY_BUFFER; + } + void setIsAsmJSArrayBuffer() { + flags |= ASMJS_ARRAY_BUFFER; + } public: ObjectElements(uint32_t capacity, uint32_t length) diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index be93391299d..01e9c9a1d08 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -25,7 +25,7 @@ namespace js { * and saved versions. If deserialization fails, the data should be * invalidated if possible. */ -static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 138); +static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 140); class XDRBuffer { public: diff --git a/js/xpconnect/shell/xpcshell.cpp b/js/xpconnect/shell/xpcshell.cpp index a878e6da993..4c0ce876d1e 100644 --- a/js/xpconnect/shell/xpcshell.cpp +++ b/js/xpconnect/shell/xpcshell.cpp @@ -1159,6 +1159,7 @@ ProcessArgsForCompartment(JSContext *cx, char **argv, int argc) case 'I': JS_ToggleOptions(cx, JSOPTION_COMPILE_N_GO); JS_ToggleOptions(cx, JSOPTION_ION); + JS_ToggleOptions(cx, JSOPTION_ASMJS); break; case 'n': JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE); diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 67c2ee1464e..7d0020daa18 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1889,6 +1889,10 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, "Memory used by the IonMonkey JIT to hold the runtime's " "generated code."); + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/asm.js-code"), + nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.asmJSCode, + "Memory used by AOT-compiled asm.js code."); + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/regexp-code"), nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpCode, "Memory used by the regexp JIT to hold generated code."); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index e53f8c5241c..ca8d1384b4c 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -752,6 +752,11 @@ pref("javascript.options.strict.debug", true); pref("javascript.options.methodjit.content", true); pref("javascript.options.methodjit.chrome", true); pref("javascript.options.ion.content", true); +#ifdef RELEASE_BUILD +pref("javascript.options.experimental_asmjs", false); +#else +pref("javascript.options.experimental_asmjs", true); +#endif pref("javascript.options.ion.parallel_compilation", true); pref("javascript.options.pccounts.content", false); pref("javascript.options.pccounts.chrome", false); From d63e8927df02d36acf09e92af7faf49d79bb704d Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Fri, 15 Mar 2013 15:23:18 +0800 Subject: [PATCH 077/202] Bug 849758 - [b2g-ril] SIM PIN requested after booting/rebooting with airplane mode on. r=vicamo. --- dom/system/gonk/ril_worker.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index 5a08327cd63..c126f80abec 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -4547,7 +4547,15 @@ RIL[REQUEST_OPERATOR] = function REQUEST_OPERATOR(length, options) { if (DEBUG) debug("Operator: " + operatorData); this._processOperator(operatorData); }; -RIL[REQUEST_RADIO_POWER] = null; +RIL[REQUEST_RADIO_POWER] = function REQUEST_RADIO_POWER(length, options) { + if (options.rilRequestError) { + return; + } + + if (this._isInitialRadioState) { + this._isInitialRadioState = false; + } +}; RIL[REQUEST_DTMF] = null; RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, options) { this._processSmsSendResult(length, options); @@ -5069,11 +5077,9 @@ RIL[UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED] = function UNSOLICITED_RESPONSE_RA // Ensure radio state at boot time. if (this._isInitialRadioState) { - this._isInitialRadioState = false; - if (radioState != RADIO_STATE_OFF) { - this.setRadioPower({on: false}); - return; - } + // Even radioState is RADIO_STATE_OFF, we still have to maually turn radio off, + // otherwise REQUEST_GET_SIM_STATUS will still report CARD_STATE_PRESENT. + this.setRadioPower({on: false}); } let newState; From 5218e39f01873409d2c3ba6662d79090f9194450 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Fri, 15 Mar 2013 06:45:31 -0500 Subject: [PATCH 078/202] Bug 851271 - fuzzy clipPath-and-shape-rendering-01.svg, scale3d-all.html, and scale3d-all-separate.html for Win8. r=mbrubeck --- layout/reftests/svg/reftest.list | 2 +- layout/reftests/transform-3d/reftest.list | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index fbc5bc1bb0e..893ff82de17 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -32,7 +32,7 @@ include svg-integration/reftest.list == clip-02a.svg clip-02-ref.svg == clip-02b.svg clip-02-ref.svg == clipPath-advanced-01.svg pass.svg -fuzzy-if(/^Windows\x20NT\x206\.[12]/.test(http.oscpu),1,2) == clipPath-and-shape-rendering-01.svg clipPath-and-shape-rendering-01-ref.svg # bug 614840 +fuzzy-if(/^Windows\x20NT\x206\.[12]/.test(http.oscpu),1,5) == clipPath-and-shape-rendering-01.svg clipPath-and-shape-rendering-01-ref.svg # bug 614840 == clipPath-and-transform-01.svg pass.svg == clipPath-basic-01.svg pass.svg == clipPath-basic-02.svg pass.svg diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index 3d7948aa4b1..e68c0cff72f 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -33,8 +33,8 @@ fails-if(Android) == rotatex-perspective-3a.html rotatex-perspective-3-ref.html skip-if(B2G) == preserve3d-4a.html green-rect.html == preserve3d-5a.html preserve3d-5-ref.html == scale3d-z.html scalez-1-ref.html -fuzzy-if(d2d&&layersGPUAccelerated,52,680) fuzzy-if(winWidget&&!layersGPUAccelerated,102,580) fails-if(Android) fuzzy-if(OSX==10.8,145,752) == scale3d-all.html scale3d-1-ref.html # subpixel AA -fuzzy-if(d2d&&layersGPUAccelerated,52,680) fuzzy-if(winWidget&&!layersGPUAccelerated,102,580) fails-if(Android) fuzzy-if(OSX==10.8,145,752) == scale3d-all-separate.html scale3d-1-ref.html # subpixel AA +fuzzy-if(d2d&&layersGPUAccelerated,52,685) fuzzy-if(winWidget&&!layersGPUAccelerated,102,580) fails-if(Android) fuzzy-if(OSX==10.8,145,752) == scale3d-all.html scale3d-1-ref.html # subpixel AA +fuzzy-if(d2d&&layersGPUAccelerated,52,685) fuzzy-if(winWidget&&!layersGPUAccelerated,102,580) fails-if(Android) fuzzy-if(OSX==10.8,145,752) == scale3d-all-separate.html scale3d-1-ref.html # subpixel AA == scale3d-xz.html scale3d-1-ref.html == translatez-1a.html translatez-1-ref.html != translatez-1b.html translatez-1-ref.html From c50072bd1cbf553f8de335f7f2e4159faa1c9602 Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Tue, 12 Mar 2013 14:50:05 -0400 Subject: [PATCH 079/202] Bug 850198, Fix null pointer checks on clipboardData when clipboard events preference is false, r=smaug --- content/base/src/nsCopySupport.cpp | 9 +++-- .../general/test_clipboard_events.html | 39 ++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/content/base/src/nsCopySupport.cpp b/content/base/src/nsCopySupport.cpp index b8ec7f4a55a..2d8f7ee97b0 100644 --- a/content/base/src/nsCopySupport.cpp +++ b/content/base/src/nsCopySupport.cpp @@ -732,8 +732,11 @@ nsCopySupport::FireClipboardEvent(int32_t aType, nsIPresShell* aPresShell, nsISe if (aType == NS_PASTE) { // Clear and mark the clipboardData as readonly. This prevents someone // from reading the clipboard contents after the paste event has fired. - clipboardData->ClearAll(); - clipboardData->SetReadOnly(); + if (clipboardData) { + clipboardData->ClearAll(); + clipboardData->SetReadOnly(); + } + return doDefault; } @@ -758,7 +761,7 @@ nsCopySupport::FireClipboardEvent(int32_t aType, nsIPresShell* aPresShell, nsISe if (NS_FAILED(rv)) { return false; } - } else { + } else if (clipboardData) { // check to see if any data was put on the data transfer. clipboardData->GetMozItemCount(&count); if (count) { diff --git a/dom/tests/mochitest/general/test_clipboard_events.html b/dom/tests/mochitest/general/test_clipboard_events.html index 684ed6f74b5..7035038334c 100644 --- a/dom/tests/mochitest/general/test_clipboard_events.html +++ b/dom/tests/mochitest/general/test_clipboard_events.html @@ -66,7 +66,8 @@ window.onfocus = function() test_input_paste_dataTransfer, test_input_paste_abort_dataTransfer, test_input_copypaste_dataTransfer_multiple, - test_input_copy_button_dataTransfer + test_input_copy_button_dataTransfer, + test_eventspref_disabled ]; // Run the main tests. This will also populate the delayedTests array @@ -571,6 +572,42 @@ function test_input_copy_button_dataTransfer() { } } +function test_eventspref_disabled() { + // Disable clipboard events + SpecialPowers.setBoolPref("dom.event.clipboardevents.enabled", false); + + var event_fired = false; + contentInput.oncut = function() { event_fired = true; }; + contentInput.oncopy = function() { event_fired = true; }; + contentInput.onpaste = function() { event_fired = true; }; + try { + selectContentInput(); + contentInput.setSelectionRange(1, 4); + synthesizeKey("x", {accelKey: 1}); + is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled"); + is(getClipboardText(), "NPU", "cut changed clipboard when preference is disabled"); + ok(!event_fired, "cut event did not fire when preference is disabled") + + event_fired = false; + contentInput.setSelectionRange(3, 6); + synthesizeKey("c", {accelKey: 1}); + is(getClipboardText(), "TEX", "copy changed clipboard when preference is disabled"); + ok(!event_fired, "copy event did not fire when preference is disabled") + + event_fired = false; + contentInput.setSelectionRange(0, 2); + synthesizeKey("v", {accelKey: 1}); + is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled"); + ok(!event_fired, "paste event did not fire when preference is disabled") + } finally { + contentInput.oncut = null; + contentInput.oncopy = null; + contentInput.onpaste = null; + } + + SpecialPowers.clearUserPref("dom.event.clipboardevents.enabled"); +} + let expectedData = []; // Check to make that synthetic events do not change the clipboard From 5cdad40dc7b800c20a38e69241835c69b9260a31 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 15 Mar 2013 12:24:11 +0000 Subject: [PATCH 080/202] Bug 681138 & bug 682837 - Disable bug106855-1.html and bug106855-2.html on all platforms for too many intermittent failures --- layout/base/tests/test_reftests_with_caret.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/layout/base/tests/test_reftests_with_caret.html b/layout/base/tests/test_reftests_with_caret.html index 5486ecf1896..1ab34fca242 100644 --- a/layout/base/tests/test_reftests_with_caret.html +++ b/layout/base/tests/test_reftests_with_caret.html @@ -137,10 +137,13 @@ if (!isWindows) { tests.push([ 'bug746993-1.html' , 'bug746993-1-ref.html' ]); // bug 746993 } +// Disabled on all platforms for too many intermittent failures +/* if (!isWindows && !isOSXMtnLion) { tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837 tests.push([ 'bug106855-2.html' , 'bug106855-1-ref.html?' ]); // bug 681138 } +*/ tests.push(function() {SpecialPowers.setBoolPref("bidi.browser.ui", true);}); From 33742c3e02e9d981fe79ef21cead53429b17c59c Mon Sep 17 00:00:00 2001 From: Yuan Xulei Date: Fri, 15 Mar 2013 08:28:51 -0400 Subject: [PATCH 081/202] Bug 844716 - Enable keyboard Apps to get/set selection range of the input field. r=timdream --- b2g/chrome/content/forms.js | 142 +++++++++++++++++++++++++++++----- b2g/components/Keyboard.jsm | 23 +++++- b2g/components/MozKeyboard.js | 72 ++++++++++++++--- b2g/components/b2g.idl | 21 +++++ 4 files changed, 227 insertions(+), 31 deletions(-) diff --git a/b2g/chrome/content/forms.js b/b2g/chrome/content/forms.js index 91d0b39b3db..65455c32066 100644 --- a/b2g/chrome/content/forms.js +++ b/b2g/chrome/content/forms.js @@ -185,9 +185,12 @@ let FormAssistant = { addEventListener("resize", this, true, false); addEventListener("submit", this, true, false); addEventListener("pagehide", this, true, false); + addEventListener("input", this, true, false); + addEventListener("keydown", this, true, false); addMessageListener("Forms:Select:Choice", this); addMessageListener("Forms:Input:Value", this); addMessageListener("Forms:Select:Blur", this); + addMessageListener("Forms:SetSelectionRange", this); }, ignoredInputTypes: new Set([ @@ -195,8 +198,8 @@ let FormAssistant = { ]), isKeyboardOpened: false, - selectionStart: 0, - selectionEnd: 0, + selectionStart: -1, + selectionEnd: -1, scrollIntoViewTimeout: null, _focusedElement: null, _documentEncoder: null, @@ -257,11 +260,14 @@ let FormAssistant = { if (isContentEditable(target)) { this.showKeyboard(this.getTopLevelEditable(target)); + this.updateSelection(); break; } - if (this.isFocusableElement(target)) + if (this.isFocusableElement(target)) { this.showKeyboard(target); + this.updateSelection(); + } break; case "pagehide": @@ -272,16 +278,17 @@ let FormAssistant = { // fall through case "blur": case "submit": - if (this.focusedElement) + if (this.focusedElement) { this.hideKeyboard(); + this.selectionStart = -1; + this.selectionEnd = -1; + } break; case 'mousedown': // We only listen for this event on the currently focused element. // When the mouse goes down, note the cursor/selection position - range = getSelectionRange(this.focusedElement); - this.selectionStart = range[0]; - this.selectionEnd = range[1]; + this.updateSelection(); break; case 'mouseup': @@ -293,6 +300,7 @@ let FormAssistant = { if (range[0] !== this.selectionStart || range[1] !== this.selectionEnd) { this.sendKeyboardState(this.focusedElement); + this.updateSelection(); } break; @@ -316,6 +324,19 @@ let FormAssistant = { }.bind(this), RESIZE_SCROLL_DELAY); } break; + + case "input": + // When the text content changes, notify the keyboard + this.updateSelection(); + break; + + case "keydown": + // We use 'setTimeout' to wait until the input element accomplishes the + // change in selection range + content.setTimeout(function() { + this.updateSelection(); + }.bind(this), 0); + break; } }, @@ -366,6 +387,14 @@ let FormAssistant = { this.setFocusedElement(null); break; } + + case "Forms:SetSelectionRange": { + let start = json.selectionStart; + let end = json.selectionEnd; + setSelectionRange(target, start, end); + this.updateSelection(); + break; + } } }, @@ -442,6 +471,19 @@ let FormAssistant = { sendAsyncMessage("Forms:Input", getJSON(element)); return true; + }, + + // Notify when the selection range changes + updateSelection: function fa_updateSelection() { + let range = getSelectionRange(this.focusedElement); + if (range[0] != this.selectionStart || range[1] != this.selectionEnd) { + this.selectionStart = range[0]; + this.selectionEnd = range[1]; + sendAsyncMessage("Forms:SelectionChange", { + selectionStart: range[0], + selectionEnd: range[1] + }); + } } }; @@ -598,17 +640,79 @@ function getSelectionRange(element) { // Get the selection range of contenteditable elements let win = element.ownerDocument.defaultView; let sel = win.getSelection(); + start = getContentEditableSelectionStart(element, sel); + end = start + getContentEditableSelectionLength(element, sel); + } + return [start, end]; + } - let range = win.document.createRange(); - range.setStart(element, 0); - range.setEnd(sel.anchorNode, sel.anchorOffset); - let encoder = FormAssistant.documentEncoder; - - encoder.setRange(range); - start = encoder.encodeToString().length; - - encoder.setRange(sel.getRangeAt(0)); - end = start + encoder.encodeToString().length; - } - return [start, end]; +function getContentEditableSelectionStart(element, selection) { + let doc = element.ownerDocument; + let range = doc.createRange(); + range.setStart(element, 0); + range.setEnd(selection.anchorNode, selection.anchorOffset); + let encoder = FormAssistant.documentEncoder; + encoder.setRange(range); + return encoder.encodeToString().length; } + +function getContentEditableSelectionLength(element, selection) { + let encoder = FormAssistant.documentEncoder; + encoder.setRange(selection.getRangeAt(0)); + return encoder.encodeToString().length; +} + +function setSelectionRange(element, start, end) { + let isPlainTextField = element instanceof HTMLInputElement || + element instanceof HTMLTextAreaElement; + + // Check the parameters + + if (!isPlainTextField && !isContentEditable(element)) { + // Skip HTMLOptionElement and HTMLSelectElement elements, as they don't + // support the operation of setSelectionRange + return; + } + + let text = isPlainTextField ? element.value : getContentEditableText(element); + let length = text.length; + if (start < 0) { + start = 0; + } + if (end > length) { + end = length; + } + if (start > end) { + start = end; + } + + if (isPlainTextField) { + // Set the selection range of and